Merge "Update the CDM device presence javadoc and overall logs"
diff --git a/ApiDocs.bp b/ApiDocs.bp
index e6525c9..c87ecde 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -132,6 +132,7 @@
"api-versions-jars-dir",
],
api_levels_sdk_type: "system",
+ extensions_info_file: ":sdk-extensions-info",
}
droidstubs {
@@ -146,6 +147,7 @@
"packages/modules/Media/apex/aidl/stable",
],
},
+ extensions_info_file: ":sdk-extensions-info",
}
/////////////////////////////////////////////////////////////////////
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 32101c7..bc2e6dd 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -420,6 +420,7 @@
"sdk-dir",
"api-versions-jars-dir",
],
+ extensions_info_file: ":sdk-extensions-info",
}
droidstubs {
@@ -432,6 +433,7 @@
"api-versions-jars-dir",
],
api_levels_sdk_type: "system",
+ extensions_info_file: ":sdk-extensions-info",
}
/////////////////////////////////////////////////////////////////////
diff --git a/cmds/am/Android.bp b/cmds/am/Android.bp
index 7bb9675..c6cc7c5 100644
--- a/cmds/am/Android.bp
+++ b/cmds/am/Android.bp
@@ -30,7 +30,7 @@
java_binary {
name: "am",
- wrapper: "am",
+ wrapper: "am.sh",
srcs: [
"src/**/*.java",
"proto/**/*.proto",
diff --git a/cmds/am/am b/cmds/am/am.sh
similarity index 100%
rename from cmds/am/am
rename to cmds/am/am.sh
diff --git a/cmds/appops/Android.bp b/cmds/appops/Android.bp
index 80f8ec6..538ae94 100644
--- a/cmds/appops/Android.bp
+++ b/cmds/appops/Android.bp
@@ -19,5 +19,5 @@
sh_binary {
name: "appops",
- src: "appops",
+ src: "appops.sh",
}
diff --git a/cmds/appops/appops b/cmds/appops/appops.sh
similarity index 100%
rename from cmds/appops/appops
rename to cmds/appops/appops.sh
diff --git a/cmds/appwidget/Android.bp b/cmds/appwidget/Android.bp
index 8049038..cd439e2 100644
--- a/cmds/appwidget/Android.bp
+++ b/cmds/appwidget/Android.bp
@@ -19,6 +19,6 @@
java_binary {
name: "appwidget",
- wrapper: "appwidget",
+ wrapper: "appwidget.sh",
srcs: ["**/*.java"],
}
diff --git a/cmds/appwidget/appwidget b/cmds/appwidget/appwidget.sh
similarity index 100%
rename from cmds/appwidget/appwidget
rename to cmds/appwidget/appwidget.sh
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 083bbf01..320e147 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -312,6 +312,8 @@
Status Idmap2Service::releaseFabricatedOverlayIterator() {
if (!frro_iter_.has_value()) {
LOG(WARNING) << "no active ffro iterator to release";
+ } else {
+ frro_iter_ = std::nullopt;
}
return ok();
}
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
index 5b0ae92..8129d99 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -17,6 +17,7 @@
#include "idmap2/CommandLineOptions.h"
#include <algorithm>
+#include <cassert>
#include <iomanip>
#include <iostream>
#include <memory>
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 6515d55..06650f6 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -17,6 +17,7 @@
#include "idmap2/Idmap.h"
#include <algorithm>
+#include <cassert>
#include <iostream>
#include <iterator>
#include <limits>
diff --git a/core/api/current.txt b/core/api/current.txt
index c2a7338..0b41068 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -41365,6 +41365,7 @@
field public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = "call_composer_picture_server_url_string";
field public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
field public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = "call_redirection_service_component_name_string";
+ field public static final String KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY = "capabilities_exempt_from_single_dc_check_int_array";
field public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL = "carrier_allow_deflect_ims_call_bool";
field public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final String KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL = "carrier_app_required_during_setup_bool";
@@ -42354,6 +42355,7 @@
field public static final int IRAT_HANDOVER_FAILED = 2194; // 0x892
field public static final int IS707B_MAX_ACCESS_PROBES = 2089; // 0x829
field public static final int IWLAN_AUTHORIZATION_REJECTED = 9003; // 0x232b
+ field public static final int IWLAN_CONGESTION = 15500; // 0x3c8c
field public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 16388; // 0x4004
field public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 16389; // 0x4005
field public static final int IWLAN_IKEV2_AUTH_FAILURE = 16385; // 0x4001
@@ -49455,6 +49457,7 @@
public class Surface implements android.os.Parcelable {
ctor public Surface(@NonNull android.view.SurfaceControl);
ctor public Surface(android.graphics.SurfaceTexture);
+ method public void clearFrameRate();
method public int describeContents();
method public boolean isValid();
method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException;
@@ -49512,6 +49515,7 @@
ctor public SurfaceControl.Transaction();
method @NonNull public android.view.SurfaceControl.Transaction addTransactionCommittedListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.SurfaceControl.TransactionCommittedListener);
method public void apply();
+ method @NonNull public android.view.SurfaceControl.Transaction clearFrameRate(@NonNull android.view.SurfaceControl);
method public void close();
method public int describeContents();
method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a87f2e2..f09244a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -573,6 +573,7 @@
field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
+ field public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO = "android:receive_ambient_trigger_audio";
field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d9e604f..a516a4a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -523,8 +523,8 @@
method @NonNull public static String operationToString(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, int);
method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void setProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName, boolean);
@@ -2383,6 +2383,16 @@
method public final boolean shouldShowComplications();
}
+ public class DreamService extends android.app.Service implements android.view.Window.Callback {
+ method @Nullable public static android.service.dreams.DreamService.DreamMetadata getDreamMetadata(@NonNull android.content.Context, @Nullable android.content.pm.ServiceInfo);
+ }
+
+ public static final class DreamService.DreamMetadata {
+ field @Nullable public final android.graphics.drawable.Drawable previewImage;
+ field @Nullable public final android.content.ComponentName settingsActivity;
+ field @NonNull public final boolean showComplications;
+ }
+
}
package android.service.notification {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e4c758e..d899dab 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3128,7 +3128,13 @@
/**
* All packages that have been loaded into the process.
*/
- public String pkgList[];
+ public String[] pkgList;
+
+ /**
+ * Additional packages loaded into the process as dependency.
+ * @hide
+ */
+ public String[] pkgDeps;
/**
* Constant for {@link #flags}: this is an app that is unable to
@@ -3509,6 +3515,7 @@
dest.writeInt(pid);
dest.writeInt(uid);
dest.writeStringArray(pkgList);
+ dest.writeStringArray(pkgDeps);
dest.writeInt(this.flags);
dest.writeInt(lastTrimLevel);
dest.writeInt(importance);
@@ -3527,6 +3534,7 @@
pid = source.readInt();
uid = source.readInt();
pkgList = source.readStringArray();
+ pkgDeps = source.readStringArray();
flags = source.readInt();
lastTrimLevel = source.readInt();
importance = source.readInt();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index db76816..ee5d2b7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -533,9 +533,8 @@
// A reusable token for other purposes, e.g. content capture, translation. It shouldn't be
// used without security checks
public IBinder shareableActivityToken;
- // The token of the initial TaskFragment that embedded this activity. Do not rely on it
- // after creation because the activity could be reparented.
- @Nullable public IBinder mInitialTaskFragmentToken;
+ // The token of the TaskFragment that embedded this activity.
+ @Nullable public IBinder mTaskFragmentToken;
int ident;
@UnsupportedAppUsage
Intent intent;
@@ -619,7 +618,7 @@
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble,
- IBinder initialTaskFragmentToken) {
+ IBinder taskFragmentToken) {
this.token = token;
this.assistToken = assistToken;
this.shareableActivityToken = shareableActivityToken;
@@ -638,7 +637,7 @@
this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo);
mActivityOptions = activityOptions;
mLaunchedFromBubble = launchedFromBubble;
- mInitialTaskFragmentToken = initialTaskFragmentToken;
+ mTaskFragmentToken = taskFragmentToken;
init();
}
@@ -1177,9 +1176,16 @@
data.mSerializedSystemFontMap = serializedSystemFontMap;
data.startRequestedElapsedTime = startRequestedElapsedTime;
data.startRequestedUptime = startRequestedUptime;
+ updateCompatOverrideScale(compatInfo);
+ CompatibilityInfo.applyOverrideScaleIfNeeded(config);
sendMessage(H.BIND_APPLICATION, data);
}
+ private void updateCompatOverrideScale(CompatibilityInfo info) {
+ CompatibilityInfo.setOverrideInvertedScale(
+ info.hasOverrideScaling() ? info.applicationInvertedScale : 1f);
+ }
+
public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = entryPoint;
@@ -1756,6 +1762,7 @@
UpdateCompatibilityData ucd = new UpdateCompatibilityData();
ucd.pkg = pkg;
ucd.info = info;
+ updateCompatOverrideScale(info);
sendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2a5916d..bc59d60 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1812,6 +1812,7 @@
*
* @hide
*/
+ @SystemApi
public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO =
"android:receive_ambient_trigger_audio";
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4c7bc6d..80121b7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1390,8 +1390,9 @@
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = new LoadedApk.ReceiverDispatcher(
- resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+ rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(),
+ resultReceiver, getOuterContext(), scheduler, null, false)
+ .getIIntentReceiver();
}
}
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
@@ -1497,8 +1498,9 @@
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = new LoadedApk.ReceiverDispatcher(resultReceiver, getOuterContext(),
- scheduler, null, false).getIIntentReceiver();
+ rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(),
+ resultReceiver, getOuterContext(), scheduler, null, false)
+ .getIIntentReceiver();
}
}
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
@@ -1616,8 +1618,9 @@
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = new LoadedApk.ReceiverDispatcher(
- resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+ rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(),
+ resultReceiver, getOuterContext(), scheduler, null, false)
+ .getIIntentReceiver();
}
}
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
@@ -1699,8 +1702,9 @@
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = new LoadedApk.ReceiverDispatcher(
- resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+ rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(),
+ resultReceiver, getOuterContext(), scheduler, null, false)
+ .getIIntentReceiver();
}
}
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
@@ -1802,7 +1806,7 @@
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = new LoadedApk.ReceiverDispatcher(
+ rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(),
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0849a6f..3620a60 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1598,8 +1598,8 @@
}
}
if (rd == null) {
- rd = new ReceiverDispatcher(r, context, handler,
- instrumentation, registered);
+ rd = new ReceiverDispatcher(mActivityThread.getApplicationThread(), r, context,
+ handler, instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
@@ -1714,6 +1714,7 @@
}
}
+ final IApplicationThread mAppThread;
final IIntentReceiver.Stub mIIntentReceiver;
@UnsupportedAppUsage
final BroadcastReceiver mReceiver;
@@ -1736,7 +1737,7 @@
boolean ordered, boolean sticky, int sendingUser) {
super(resultCode, resultData, resultExtras,
mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
- sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
+ sticky, mAppThread.asBinder(), sendingUser, intent.getFlags());
mCurIntent = intent;
mOrdered = ordered;
}
@@ -1811,13 +1812,14 @@
}
}
- ReceiverDispatcher(BroadcastReceiver receiver, Context context,
- Handler activityThread, Instrumentation instrumentation,
+ ReceiverDispatcher(IApplicationThread appThread, BroadcastReceiver receiver,
+ Context context, Handler activityThread, Instrumentation instrumentation,
boolean registered) {
if (activityThread == null) {
throw new NullPointerException("Handler must not be null");
}
+ mAppThread = appThread;
mIIntentReceiver = new InnerReceiver(this, !registered);
mReceiver = receiver;
mContext = context;
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 2b245aa..5c29eb3 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -95,8 +95,11 @@
} else {
mId = null;
}
- mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mName = getTrimmedString(mName.toString());
+ if (in.readByte() != 0) {
+ mName = getTrimmedString(in.readString());
+ } else {
+ mName = "";
+ }
if (in.readByte() != 0) {
mDescription = getTrimmedString(in.readString());
} else {
@@ -122,7 +125,12 @@
} else {
dest.writeByte((byte) 0);
}
- TextUtils.writeToParcel(mName.toString(), dest, flags);
+ if (mName != null) {
+ dest.writeByte((byte) 1);
+ dest.writeString(mName.toString());
+ } else {
+ dest.writeByte((byte) 0);
+ }
if (mDescription != null) {
dest.writeByte((byte) 1);
dest.writeString(mDescription);
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index ae0fc09..6d28972 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -165,7 +165,7 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
- // LINT.ThenChange(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt)
+ // LINT.ThenChange(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt)
/**
* Default disable flags for setup
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 397c8e0..39d77c4 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -462,14 +462,31 @@
/** @hide */
public void scale(float scale) {
- mBounds.scale(scale);
- mMaxBounds.scale(scale);
+ scaleBounds(scale, mBounds);
+ scaleBounds(scale, mMaxBounds);
if (mAppBounds != null) {
- mAppBounds.scale(scale);
+ scaleBounds(scale, mAppBounds);
}
}
/**
+ * Size based scaling. This avoid inconsistent length when rounding 4 sides.
+ * E.g. left=12, right=18, scale=0.8. The scaled width can be:
+ * int((right - left) * scale + 0.5) = int(4.8 + 0.5) = 5
+ * But with rounding both left and right, the width will be inconsistent:
+ * int(right * scale + 0.5) - int(left * scale + 0.5) = int(14.9) - int(10.1) = 4
+ * @hide
+ */
+ private static void scaleBounds(float scale, Rect bounds) {
+ final int w = bounds.width();
+ final int h = bounds.height();
+ bounds.left = (int) (bounds.left * scale + .5f);
+ bounds.top = (int) (bounds.top * scale + .5f);
+ bounds.right = bounds.left + (int) (w * scale + .5f);
+ bounds.bottom = bounds.top + (int) (h * scale + .5f);
+ }
+
+ /**
* Copies the fields from delta into this Configuration object, keeping
* track of which ones have changed. Any undefined fields in {@code delta}
* are ignored and not copied in to the current Configuration.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c12e26b..5769728 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7394,6 +7394,9 @@
* The grantee app will receive the {@link android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED}
* broadcast when access to a key is granted.
*
+ * Starting from {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} throws an
+ * {@link IllegalArgumentException} if {@code alias} doesn't correspond to an existing key.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate chooser.
* @param alias The alias of the key to grant access to.
@@ -7460,6 +7463,9 @@
* The grantee app will receive the {@link android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED}
* broadcast when access to a key is revoked.
*
+ * Starting from {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} throws an
+ * {@link IllegalArgumentException} if {@code alias} doesn't correspond to an existing key.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate chooser.
* @param alias The alias of the key to revoke access from.
@@ -7490,6 +7496,9 @@
* pair for authentication to Wifi networks. The key can then be used in configurations passed
* to {@link android.net.wifi.WifiManager#addNetwork}.
*
+ * Starting from {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} throws an
+ * {@link IllegalArgumentException} if {@code alias} doesn't correspond to an existing key.
+ *
* @param alias The alias of the key pair.
* @return {@code true} if the operation was set successfully, {@code false} otherwise.
*
@@ -7513,6 +7522,9 @@
* pair for authentication to Wifi networks. Configured networks using this key won't be able to
* authenticate.
*
+ * Starting from {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} throws an
+ * {@link IllegalArgumentException} if {@code alias} doesn't correspond to an existing key.
+ *
* @param alias The alias of the key pair.
* @return {@code true} if the operation was set successfully, {@code false} otherwise.
*
@@ -8677,7 +8689,6 @@
* and no accounts.
*
* @param who the component name to be registered as device owner.
- * @param ownerName the human readable name of the institution that owns this device.
* @param userId ID of the user on which the device owner runs.
*
* @return whether the package was successfully registered as the device owner.
@@ -8689,11 +8700,10 @@
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- public boolean setDeviceOwner(@NonNull ComponentName who, @Nullable String ownerName,
- @UserIdInt int userId) {
+ public boolean setDeviceOwner(@NonNull ComponentName who, @UserIdInt int userId) {
if (mService != null) {
try {
- return mService.setDeviceOwner(who, ownerName, userId,
+ return mService.setDeviceOwner(who, userId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ true);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -8703,7 +8713,7 @@
}
/**
- * Same as {@link #setDeviceOwner(ComponentName, String, int)}, but without setting the profile
+ * Same as {@link #setDeviceOwner(ComponentName, int)}, but without setting the profile
* owner on current user when running on headless system user mode - should be used only by
* testing infra.
*
@@ -8712,10 +8722,10 @@
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public boolean setDeviceOwnerOnly(
- @NonNull ComponentName who, @Nullable String ownerName, @UserIdInt int userId) {
+ @NonNull ComponentName who, @UserIdInt int userId) {
if (mService != null) {
try {
- return mService.setDeviceOwner(who, ownerName, userId,
+ return mService.setDeviceOwner(who, userId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ false);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index fea7770..ab8d50b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -168,7 +168,7 @@
void reportKeyguardDismissed(int userHandle);
void reportKeyguardSecured(int userHandle);
- boolean setDeviceOwner(in ComponentName who, String ownerName, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
+ boolean setDeviceOwner(in ComponentName who, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
boolean hasDeviceOwner();
String getDeviceOwnerName();
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 5a3ad31..1c490be 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Parcel;
@@ -40,6 +41,7 @@
@Override
public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
client.updatePendingActivityConfiguration(token, mConfiguration);
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index b3e34b3..c09461c 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -23,6 +23,7 @@
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
+import android.content.res.CompatibilityInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Trace;
@@ -56,6 +57,7 @@
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mConfig);
mActivityClientRecord = client.prepareRelaunchActivity(token, mPendingResults,
mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow);
}
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 2acff49..49a1c66 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Parcel;
@@ -34,6 +35,7 @@
@Override
public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
client.updatePendingConfiguration(mConfiguration);
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 03d6e27..7e4db81 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -30,6 +30,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.BaseBundle;
import android.os.Bundle;
@@ -81,6 +82,8 @@
public void preExecute(ClientTransactionHandler client, IBinder token) {
client.countLaunchingActivities(1);
client.updateProcessState(mProcState, false);
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mCurConfig);
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mOverrideConfig);
client.updatePendingConfiguration(mCurConfig);
if (mActivityClientController != null) {
ActivityClient.setActivityClientController(mActivityClientController);
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 2893ff2..f13bd74 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Parcel;
@@ -40,6 +41,7 @@
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
client.updatePendingActivityConfiguration(token, mConfiguration);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f593ed9..43fa617 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3920,6 +3920,12 @@
* that has been started. This is sent as a foreground
* broadcast, since it is part of a visible user interaction; be as quick
* as possible when handling it.
+ *
+ * <p>
+ * <b>Note:</b> The user's actual state might have changed by the time the broadcast is
+ * received. For example, the user could have been removed, started or stopped already,
+ * regardless of which broadcast you receive. Because of that, receivers should always check
+ * the current state of the user.
* @hide
*/
public static final String ACTION_USER_STARTED =
@@ -3937,6 +3943,12 @@
* {@link #ACTION_USER_STOPPING}. It is <b>not</b> generally safe to use with
* other user state broadcasts since those are foreground broadcasts so can
* execute in a different order.
+ *
+ * <p>
+ * <b>Note:</b> The user's actual state might have changed by the time the broadcast is
+ * received. For example, the user could have been removed, started or stopped already,
+ * regardless of which broadcast you receive. Because of that, receivers should always check
+ * the current state of the user.
* @hide
*/
public static final String ACTION_USER_STARTING =
@@ -3955,6 +3967,11 @@
* {@link #ACTION_USER_STARTING}. It is <b>not</b> generally safe to use with
* other user state broadcasts since those are foreground broadcasts so can
* execute in a different order.
+ * <p>
+ * <b>Note:</b> The user's actual state might have changed by the time the broadcast is
+ * received. For example, the user could have been removed, started or stopped already,
+ * regardless of which broadcast you receive. Because of that, receivers should always check
+ * the current state of the user.
* @hide
*/
public static final String ACTION_USER_STOPPING =
@@ -3967,6 +3984,12 @@
* specific package. This is only sent to registered receivers, not manifest
* receivers. It is sent to all running users <em>except</em> the one that
* has just been stopped (which is no longer running).
+ *
+ * <p>
+ * <b>Note:</b> The user's actual state might have changed by the time the broadcast is
+ * received. For example, the user could have been removed, started or stopped already,
+ * regardless of which broadcast you receive. Because of that, receivers should always check
+ * the current state of the user.
* @hide
*/
@TestApi
@@ -3999,6 +4022,12 @@
* It is sent to all running users.
* You must hold
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
+ *
+ * <p>
+ * <b>Note:</b> The user's actual state might have changed by the time the broadcast is
+ * received. For example, the user could have been removed, started or stopped already,
+ * regardless of which broadcast you receive. Because of that, receivers should always check
+ * the current state of the user.
* @hide
*/
@SystemApi
@@ -4009,6 +4038,12 @@
* Broadcast Action: Sent when the credential-encrypted private storage has
* become unlocked for the target user. This is only sent to registered
* receivers, not manifest receivers.
+ *
+ * <p>
+ * <b>Note:</b> The user's actual state might have changed by the time the broadcast is
+ * received. For example, the user could have been removed, started or stopped already,
+ * regardless of which broadcast you receive. Because of that, receivers should always check
+ * the current state of the user.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index e146bb9..6ce2242 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -29,6 +29,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import android.util.MergedConfiguration;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.MotionEvent;
@@ -111,6 +112,9 @@
*/
public final float applicationInvertedScale;
+ /** The process level override inverted scale. See {@link #HAS_OVERRIDE_SCALING}. */
+ private static float sOverrideInvertedScale = 1f;
+
@UnsupportedAppUsage
@Deprecated
public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
@@ -125,6 +129,15 @@
if (appInfo.targetSdkVersion < VERSION_CODES.O) {
compatFlags |= NEEDS_COMPAT_RES;
}
+ if (overrideScale != 1.0f) {
+ applicationScale = overrideScale;
+ applicationInvertedScale = 1.0f / overrideScale;
+ applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE
+ * applicationInvertedScale) + .5f);
+ mCompatibilityFlags = NEVER_NEEDS_COMPAT | HAS_OVERRIDE_SCALING;
+ // Override scale has the highest priority. So ignore other compatibility attributes.
+ return;
+ }
if (appInfo.requiresSmallestWidthDp != 0 || appInfo.compatibleWidthLimitDp != 0
|| appInfo.largestWidthLimitDp != 0) {
// New style screen requirements spec.
@@ -254,13 +267,7 @@
compatFlags |= NEVER_NEEDS_COMPAT;
}
- if (overrideScale != 1.0f) {
- applicationScale = overrideScale;
- applicationInvertedScale = 1.0f / overrideScale;
- applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE
- * applicationInvertedScale) + .5f);
- compatFlags |= HAS_OVERRIDE_SCALING;
- } else if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+ if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
applicationDensity = DisplayMetrics.DENSITY_DEVICE;
applicationScale = 1.0f;
applicationInvertedScale = 1.0f;
@@ -296,9 +303,14 @@
*/
@UnsupportedAppUsage
public boolean isScalingRequired() {
- return (mCompatibilityFlags & (SCALING_REQUIRED | HAS_OVERRIDE_SCALING)) != 0;
+ return (mCompatibilityFlags & SCALING_REQUIRED) != 0;
}
-
+
+ /** Returns {@code true} if {@link #sOverrideInvertedScale} should be set. */
+ public boolean hasOverrideScaling() {
+ return (mCompatibilityFlags & HAS_OVERRIDE_SCALING) != 0;
+ }
+
@UnsupportedAppUsage
public boolean supportsScreen() {
return (mCompatibilityFlags&NEEDS_SCREEN_COMPAT) == 0;
@@ -513,7 +525,19 @@
}
}
+ /** Applies the compatibility adjustment to the display metrics. */
+ public void applyDisplayMetricsIfNeeded(DisplayMetrics inoutDm, boolean applyToSize) {
+ if (hasOverrideScale()) {
+ scaleDisplayMetrics(sOverrideInvertedScale, inoutDm, applyToSize);
+ return;
+ }
+ if (!equals(DEFAULT_COMPATIBILITY_INFO)) {
+ applyToDisplayMetrics(inoutDm);
+ }
+ }
+
public void applyToDisplayMetrics(DisplayMetrics inoutDm) {
+ if (hasOverrideScale()) return;
if (!supportsScreen()) {
// This is a larger screen device and the app is not
// compatible with large screens, so diddle it.
@@ -524,18 +548,26 @@
}
if (isScalingRequired()) {
- float invertedRatio = applicationInvertedScale;
- inoutDm.density = inoutDm.noncompatDensity * invertedRatio;
- inoutDm.densityDpi = (int)((inoutDm.noncompatDensityDpi * invertedRatio) + .5f);
- inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio;
- inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio;
- inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio;
+ scaleDisplayMetrics(applicationInvertedScale, inoutDm, true /* applyToSize */);
+ }
+ }
+
+ /** Scales the density of the given display metrics. */
+ private static void scaleDisplayMetrics(float invertedRatio, DisplayMetrics inoutDm,
+ boolean applyToSize) {
+ inoutDm.density = inoutDm.noncompatDensity * invertedRatio;
+ inoutDm.densityDpi = (int) ((inoutDm.noncompatDensityDpi * invertedRatio) + .5f);
+ inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio;
+ inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio;
+ inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio;
+ if (applyToSize) {
inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f);
inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f);
}
}
public void applyToConfiguration(int displayDensity, Configuration inoutConfig) {
+ if (hasOverrideScale()) return;
if (!supportsScreen()) {
// This is a larger screen device and the app is not
// compatible with large screens, so we are forcing it to
@@ -549,12 +581,45 @@
}
inoutConfig.densityDpi = displayDensity;
if (isScalingRequired()) {
- float invertedRatio = applicationInvertedScale;
- inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f);
- inoutConfig.windowConfiguration.scale(invertedRatio);
+ scaleConfiguration(applicationInvertedScale, inoutConfig);
}
}
+ /** Scales the density and bounds of the given configuration. */
+ public static void scaleConfiguration(float invertedRatio, Configuration inoutConfig) {
+ inoutConfig.densityDpi = (int) ((inoutConfig.densityDpi * invertedRatio) + .5f);
+ inoutConfig.windowConfiguration.scale(invertedRatio);
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static void applyOverrideScaleIfNeeded(Configuration config) {
+ if (!hasOverrideScale()) return;
+ scaleConfiguration(sOverrideInvertedScale, config);
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static void applyOverrideScaleIfNeeded(MergedConfiguration mergedConfig) {
+ if (!hasOverrideScale()) return;
+ scaleConfiguration(sOverrideInvertedScale, mergedConfig.getGlobalConfiguration());
+ scaleConfiguration(sOverrideInvertedScale, mergedConfig.getOverrideConfiguration());
+ scaleConfiguration(sOverrideInvertedScale, mergedConfig.getMergedConfiguration());
+ }
+
+ /** Returns {@code true} if this process is in a environment with override scale. */
+ private static boolean hasOverrideScale() {
+ return sOverrideInvertedScale != 1f;
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static void setOverrideInvertedScale(float invertedRatio) {
+ sOverrideInvertedScale = invertedRatio;
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static float getOverrideInvertedScale() {
+ return sOverrideInvertedScale;
+ }
+
/**
* Compute the frame Rect for applications runs under compatibility mode.
*
@@ -632,6 +697,10 @@
sb.append(applicationScale);
sb.append("x");
}
+ if (hasOverrideScaling()) {
+ sb.append(" overrideInvScale=");
+ sb.append(applicationInvertedScale);
+ }
if (!supportsScreen()) {
sb.append(" resizing");
}
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
new file mode 100644
index 0000000..a281cd5
--- /dev/null
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -0,0 +1,10 @@
+package android.credentials;
+
+/**
+ * Mediator between apps and credential manager service implementations.
+ *
+ * {@hide}
+ */
+oneway interface ICredentialManager {
+ void getCredential();
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index d6d3a97..dff2f7e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1204,6 +1204,20 @@
* to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
* {@link #onCameraAvailable}.</p>
*
+ * <p>Limitation: Opening a logical camera disables the {@link #onPhysicalCameraAvailable}
+ * and {@link #onPhysicalCameraUnavailable} callbacks for its physical cameras. For example,
+ * if app A opens the camera device:</p>
+ *
+ * <ul>
+ *
+ * <li>All apps subscribing to ActivityCallback get {@link #onCameraUnavailable}.</li>
+ *
+ * <li>No app (including app A) subscribing to ActivityCallback gets
+ * {@link #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable}, because
+ * the logical camera is unavailable (some app is using it).</li>
+ *
+ * </ul>
+ *
* <p>The default implementation of this method does nothing.</p>
*
* @param cameraId The unique identifier of the logical multi-camera.
@@ -1221,11 +1235,24 @@
* A previously-available physical camera has become unavailable for use.
*
* <p>By default, all of the physical cameras of a logical multi-camera are
- * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
- * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
- * multi-camera is invoked. If some specific physical cameras are unavailable
- * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
- * {@link #onCameraAvailable}.</p>
+ * unavailable if the logical camera itself is unavailable.
+ * No availability callbacks will be called for any of the physical
+ * cameras of its parent logical multi-camera, when {@link #onCameraUnavailable} for
+ * the logical multi-camera is invoked.</p>
+ *
+ * <p>Limitation: Opening a logical camera disables the {@link #onPhysicalCameraAvailable}
+ * and {@link #onPhysicalCameraUnavailable} callbacks for its physical cameras. For example,
+ * if app A opens the camera device:</p>
+ *
+ * <ul>
+ *
+ * <li>All apps subscribing to ActivityCallback get {@link #onCameraUnavailable}.</li>
+ *
+ * <li>No app (including app A) subscribing to ActivityCallback gets
+ * {@link #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable}, because
+ * the logical camera is unavailable (some app is using it).</li>
+ *
+ * </ul>
*
* <p>The default implementation of this method does nothing.</p>
*
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index ba0546a..3979c6c 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -171,7 +171,7 @@
mPassword = password;
mRsaPrivateKey = rsaPrivateKey;
mUserCert = userCert;
- mProxyInfo = new ProxyInfo(proxyInfo);
+ mProxyInfo = (proxyInfo == null) ? null : new ProxyInfo(proxyInfo);
// UnmodifiableList doesn't make a defensive copy by default.
mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 7e355d9..e845ffa 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -934,6 +934,12 @@
* you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
* Otherwise, this method might throw an exception or return {@code null}.
*
+ * <p><b>Warning: </b> the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #getParcelable(String)} instead.
+ *
* @param key a String, or {@code null}
* @param clazz The type of the object expected
* @return a Parcelable value, or {@code null}
@@ -990,6 +996,13 @@
* you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
* Otherwise, this method might throw an exception or return {@code null}.
*
+ * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+ * the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #getParcelableArray(String)} instead.
+ *
* @param key a String, or {@code null}
* @param clazz The type of the items inside the array. This is only verified when unparceling.
* @return a Parcelable[] value, or {@code null}
@@ -1054,6 +1067,13 @@
* you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
* Otherwise, this method might throw an exception or return {@code null}.
*
+ * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+ * the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #getParcelableArrayList(String)} instead.
+ *
* @param key a String, or {@code null}
* @param clazz The type of the items inside the array list. This is only verified when
* unparceling.
@@ -1105,6 +1125,13 @@
* <li>The object is not of type {@code clazz}.
* </ul>
*
+ * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+ * the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #getSparseParcelableArray(String)} instead.
+ *
* @param key a String, or null
* @param clazz The type of the items inside the sparse array. This is only verified when
* unparceling.
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 1f3108a..d84037f 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -16,6 +16,7 @@
# Multiuser
per-file IUser* = file:/MULTIUSER_OWNERS
per-file User* = file:/MULTIUSER_OWNERS
+per-file NewUser* = file:/MULTIUSER_OWNERS
# Binder
per-file BadParcelableException.java = file:platform/frameworks/native:/libs/binder/OWNERS
diff --git a/core/java/android/os/PackageTagsList.java b/core/java/android/os/PackageTagsList.java
index 4a81dc6..df99074 100644
--- a/core/java/android/os/PackageTagsList.java
+++ b/core/java/android/os/PackageTagsList.java
@@ -26,6 +26,7 @@
import com.android.internal.annotations.Immutable;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
@@ -140,6 +141,17 @@
return true;
}
+ /**
+ * Returns a list of packages.
+ *
+ * @deprecated Do not use.
+ * @hide
+ */
+ @Deprecated
+ public @NonNull Collection<String> getPackages() {
+ return new ArrayList<>(mPackageTags.keySet());
+ }
+
public static final @NonNull Parcelable.Creator<PackageTagsList> CREATOR =
new Parcelable.Creator<PackageTagsList>() {
@SuppressWarnings("unchecked")
@@ -217,7 +229,7 @@
if (j > 0) {
pw.print(", ");
}
- if (attributionTag.startsWith(packageName)) {
+ if (attributionTag != null && attributionTag.startsWith(packageName)) {
pw.print(attributionTag.substring(packageName.length()));
} else {
pw.print(attributionTag);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 80201d3..9189c6c 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3266,6 +3266,13 @@
* Same as {@link #readList(List, ClassLoader)} but accepts {@code clazz} parameter as
* the type required for each item.
*
+ * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+ * the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #readList(List, ClassLoader)} instead.
+ *
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
* is not an instance of that class or any of its children classes or there was an error
* trying to instantiate an element.
@@ -3494,6 +3501,13 @@
* Same as {@link #readArrayList(ClassLoader)} but accepts {@code clazz} parameter as
* the type required for each item.
*
+ * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+ * the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #readArrayList(ClassLoader)} instead.
+ *
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
* is not an instance of that class or any of its children classes or there was an error
* trying to instantiate an element.
@@ -3528,6 +3542,13 @@
* Same as {@link #readArray(ClassLoader)} but accepts {@code clazz} parameter as
* the type required for each item.
*
+ * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+ * the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #readArray(ClassLoader)} instead.
+ *
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
* is not an instance of that class or any of its children classes or there was an error
* trying to instantiate an element.
@@ -3561,6 +3582,13 @@
* Same as {@link #readSparseArray(ClassLoader)} but accepts {@code clazz} parameter as
* the type required for each item.
*
+ * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+ * the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #readSparseArray(ClassLoader)} instead.
+ *
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
* is not an instance of that class or any of its children classes or there was an error
* trying to instantiate an element.
@@ -3878,6 +3906,13 @@
* Same as {@link #readParcelableList(List, ClassLoader)} but accepts {@code clazz} parameter as
* the type required for each item.
*
+ * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+ * the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #readParcelableList(List, ClassLoader)} instead.
+ *
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
* is not an instance of that class or any of its children classes or there was an error
* trying to instantiate an element.
@@ -4775,6 +4810,12 @@
* Same as {@link #readParcelable(ClassLoader)} but accepts {@code clazz} parameter as the type
* required for each item.
*
+ * <p><b>Warning: </b> the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #readParcelable(ClassLoader)} instead.
+ *
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
* is not an instance of that class or any of its children classes or there was an error
* trying to instantiate an element.
@@ -4843,6 +4884,12 @@
* Same as {@link #readParcelableCreator(ClassLoader)} but accepts {@code clazz} parameter
* as the required type.
*
+ * <p><b>Warning: </b> the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #readParcelableCreator(ClassLoader) instead.
+ *
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
* is not an instance of that class or any of its children classes or there there was an error
* trying to read the {@link Parcelable.Creator}.
@@ -4979,6 +5026,12 @@
* Same as {@link #readParcelableArray(ClassLoader)} but accepts {@code clazz} parameter as
* the type required for each item.
*
+ * <p><b>Warning: </b> the class that implements {@link Parcelable} has to be the immediately
+ * enclosing class of the runtime type of its CREATOR field (that is,
+ * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+ * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+ * CREATOR, use the deprecated {@link #readParcelableArray(ClassLoader)} instead.
+ *
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
* is not an instance of that class or any of its children classes or there was an error
* trying to instantiate an element.
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index b0c5d83..6f5212c 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.TestApi;
import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
@@ -1124,7 +1125,8 @@
* @hide
*/
@Nullable
- public static DreamMetadata getDreamMetadata(Context context,
+ @TestApi
+ public static DreamMetadata getDreamMetadata(@NonNull Context context,
@Nullable ServiceInfo serviceInfo) {
if (serviceInfo == null) return null;
@@ -1183,7 +1185,8 @@
}
}
- private static ComponentName convertToComponentName(String flattenedString,
+ @Nullable
+ private static ComponentName convertToComponentName(@Nullable String flattenedString,
ServiceInfo serviceInfo) {
if (flattenedString == null) {
return null;
@@ -1193,7 +1196,17 @@
return new ComponentName(serviceInfo.packageName, flattenedString);
}
- return ComponentName.unflattenFromString(flattenedString);
+ // Ensure that the component is from the same package as the dream service. If not,
+ // treat the component as invalid and return null instead.
+ final ComponentName cn = ComponentName.unflattenFromString(flattenedString);
+ if (cn == null) return null;
+ if (!cn.getPackageName().equals(serviceInfo.packageName)) {
+ Log.w(TAG,
+ "Inconsistent package name in component: " + cn.getPackageName()
+ + ", should be: " + serviceInfo.packageName);
+ return null;
+ }
+ return cn;
}
/**
@@ -1489,6 +1502,7 @@
*
* @hide
*/
+ @TestApi
public static final class DreamMetadata {
@Nullable
public final ComponentName settingsActivity;
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index 59f1e8e..55e2c17 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -40,5 +40,5 @@
void closeSystemDialogs();
void onLockscreenShown();
void destroy();
- void updateVisibleActivityInfo(in VisibleActivityInfo visibleActivityInfo, int type);
+ void notifyVisibleActivityInfoChanged(in VisibleActivityInfo visibleActivityInfo, int type);
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index f0f16db..82e1d5f0 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -361,9 +361,10 @@
}
@Override
- public void updateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) {
+ public void notifyVisibleActivityInfoChanged(VisibleActivityInfo visibleActivityInfo,
+ int type) {
mHandlerCaller.sendMessage(
- mHandlerCaller.obtainMessageIO(MSG_UPDATE_VISIBLE_ACTIVITY_INFO, type,
+ mHandlerCaller.obtainMessageIO(MSG_NOTIFY_VISIBLE_ACTIVITY_INFO_CHANGED, type,
visibleActivityInfo));
}
};
@@ -857,7 +858,7 @@
static final int MSG_SHOW = 106;
static final int MSG_HIDE = 107;
static final int MSG_ON_LOCKSCREEN_SHOWN = 108;
- static final int MSG_UPDATE_VISIBLE_ACTIVITY_INFO = 109;
+ static final int MSG_NOTIFY_VISIBLE_ACTIVITY_INFO_CHANGED = 109;
static final int MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK = 110;
static final int MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK = 111;
@@ -945,12 +946,13 @@
if (DEBUG) Log.d(TAG, "onLockscreenShown");
onLockscreenShown();
break;
- case MSG_UPDATE_VISIBLE_ACTIVITY_INFO:
+ case MSG_NOTIFY_VISIBLE_ACTIVITY_INFO_CHANGED:
if (DEBUG) {
- Log.d(TAG, "doUpdateVisibleActivityInfo: visibleActivityInfo=" + msg.obj
- + " type=" + msg.arg1);
+ Log.d(TAG,
+ "doNotifyVisibleActivityInfoChanged: visibleActivityInfo=" + msg.obj
+ + " type=" + msg.arg1);
}
- doUpdateVisibleActivityInfo((VisibleActivityInfo) msg.obj, msg.arg1);
+ doNotifyVisibleActivityInfoChanged((VisibleActivityInfo) msg.obj, msg.arg1);
break;
case MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK:
if (DEBUG) {
@@ -1178,7 +1180,8 @@
}
}
- private void doUpdateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) {
+ private void doNotifyVisibleActivityInfoChanged(VisibleActivityInfo visibleActivityInfo,
+ int type) {
if (mVisibleActivityCallbacks.isEmpty()) {
return;
@@ -1186,11 +1189,11 @@
switch (type) {
case VisibleActivityInfo.TYPE_ACTIVITY_ADDED:
- informVisibleActivityChanged(visibleActivityInfo, type);
+ notifyVisibleActivityChanged(visibleActivityInfo, type);
mVisibleActivityInfos.add(visibleActivityInfo);
break;
case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED:
- informVisibleActivityChanged(visibleActivityInfo, type);
+ notifyVisibleActivityChanged(visibleActivityInfo, type);
mVisibleActivityInfos.remove(visibleActivityInfo);
break;
}
@@ -1235,7 +1238,7 @@
}
}
- private void informVisibleActivityChanged(VisibleActivityInfo visibleActivityInfo, int type) {
+ private void notifyVisibleActivityChanged(VisibleActivityInfo visibleActivityInfo, int type) {
for (Map.Entry<VisibleActivityCallback, Executor> e :
mVisibleActivityCallbacks.entrySet()) {
final Executor executor = e.getValue();
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index f65a69a..0ba3072 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -723,9 +723,9 @@
outMetrics.noncompatWidthPixels = outMetrics.widthPixels = width;
outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height;
- if (!compatInfo.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
- compatInfo.applyToDisplayMetrics(outMetrics);
- }
+ // Apply to size if the configuration is EMPTY because the size is from real display info.
+ final boolean applyToSize = configuration != null && appBounds == null;
+ compatInfo.applyDisplayMetricsIfNeeded(outMetrics, applyToSize);
}
// For debugging purposes
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 14691b3..a1ece92 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -116,9 +116,6 @@
// The motion event is not from a stylus event, ignore it.
return false;
}
- if (!mImm.isStylusHandwritingAvailable()) {
- return false;
- }
mState = new State(motionEvent);
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -280,6 +277,13 @@
mHandwritingAreasTracker.updateHandwritingAreaForView(view);
}
+ private static boolean shouldTriggerStylusHandwritingForView(@NonNull View view) {
+ if (!view.isAutoHandwritingEnabled()) {
+ return false;
+ }
+ return view.isStylusHandwritingAvailable();
+ }
+
/**
* Given the location of the stylus event, return the best candidate view to initialize
* handwriting mode.
@@ -296,9 +300,10 @@
// whether the connectedView's boundary contains the initial stylus position. If true,
// directly return the connectedView.
final View connectedView = getConnectedView();
- if (connectedView != null && connectedView.isAutoHandwritingEnabled()) {
+ if (connectedView != null) {
Rect handwritingArea = getViewHandwritingArea(connectedView);
- if (isInHandwritingArea(handwritingArea, x, y, connectedView)) {
+ if (isInHandwritingArea(handwritingArea, x, y, connectedView)
+ && shouldTriggerStylusHandwritingForView(connectedView)) {
final float distance = distance(handwritingArea, x, y);
if (distance == 0f) return connectedView;
@@ -313,10 +318,12 @@
for (HandwritableViewInfo viewInfo : handwritableViewInfos) {
final View view = viewInfo.getView();
final Rect handwritingArea = viewInfo.getHandwritingArea();
- if (!isInHandwritingArea(handwritingArea, x, y, view)) continue;
+ if (!isInHandwritingArea(handwritingArea, x, y, view)
+ || !shouldTriggerStylusHandwritingForView(view)) {
+ continue;
+ }
final float distance = distance(handwritingArea, x, y);
-
if (distance == 0f) return view;
if (distance < minDistance) {
minDistance = distance;
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index b48b525..a5ac948 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -181,7 +181,8 @@
if (!view.hasImeFocus() || !view.hasWindowFocus()) {
return;
}
- if (DEBUG) Log.d(TAG, "onViewFocusChanged, view=" + view + ", mServedView=" + mServedView);
+ if (DEBUG) Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view)
+ + ", mServedView=" + InputMethodDebug.dumpViewInfo(mServedView));
// We don't need to track the next served view when the view lost focus here because:
// 1) The current view focus may be cleared temporary when in touch mode, closing input
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 45d28da..9f426a1 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -351,20 +351,6 @@
return insets;
}
- // TODO: Remove this once the task bar is treated as navigation bar.
- public Insets calculateInsetsWithInternalTypes(Rect frame, @InternalInsetsType int[] types,
- boolean ignoreVisibility) {
- Insets insets = Insets.NONE;
- for (int i = types.length - 1; i >= 0; i--) {
- InsetsSource source = mSources[types[i]];
- if (source == null) {
- continue;
- }
- insets = Insets.max(source.calculateInsets(frame, ignoreVisibility), insets);
- }
- return insets;
- }
-
public Insets calculateInsets(Rect frame, @InsetsType int types,
InsetsVisibilities overrideVisibilities) {
Insets insets = Insets.NONE;
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 6b21bed..2208f89 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -985,6 +985,8 @@
*
* @throws IllegalArgumentException If <code>frameRate</code>, <code>compatibility</code> or
* <code>changeFrameRateStrategy</code> are invalid.
+ *
+ * @see #clearFrameRate()
*/
public void setFrameRate(@FloatRange(from = 0.0) float frameRate,
@FrameRateCompatibility int compatibility,
@@ -996,7 +998,33 @@
if (error == -EINVAL) {
throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()");
} else if (error != 0) {
- throw new RuntimeException("Failed to set frame rate on Surface");
+ throw new RuntimeException("Failed to set frame rate on Surface. Native error: "
+ + error);
+ }
+ }
+ }
+
+ /**
+ * Clears the frame rate which was set for this surface.
+ *
+ * <p>This is equivalent to calling {@link #setFrameRate(float, int, int)} using {@code 0} for
+ * {@code frameRate}.
+ * <p>Note that this only has an effect for surfaces presented on the display. If this
+ * surface is consumed by something other than the system compositor, e.g. a media
+ * codec, this call has no effect.</p>
+ *
+ * @see #setFrameRate(float, int, int)
+ */
+ public void clearFrameRate() {
+ synchronized (mLock) {
+ checkNotReleasedLocked();
+ // The values FRAME_RATE_COMPATIBILITY_DEFAULT and CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
+ // are ignored because the value of frameRate is 0
+ int error = nativeSetFrameRate(mNativeObject, 0,
+ FRAME_RATE_COMPATIBILITY_DEFAULT, CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ if (error != 0) {
+ throw new RuntimeException("Failed to clear the frame rate on Surface. Native error"
+ + ": " + error);
}
}
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2a45fa5..aef38eb 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3612,6 +3612,8 @@
* black screen for a second or two. This parameter is
* ignored when <code>frameRate</code> is 0.
* @return This transaction object.
+ *
+ * @see #clearFrameRate(SurfaceControl)
*/
@NonNull
public Transaction setFrameRate(@NonNull SurfaceControl sc,
@@ -3625,6 +3627,30 @@
}
/**
+ * Clears the frame rate which was set for the surface {@link SurfaceControl}.
+ *
+ * <p>This is equivalent to calling {@link #setFrameRate(SurfaceControl, float, int, int)}
+ * using {@code 0} for {@code frameRate}.
+ * <p>
+ * Note that this only has an effect for surfaces presented on the display. If this
+ * surface is consumed by something other than the system compositor, e.g. a media
+ * codec, this call has no effect.
+ *
+ * @param sc The SurfaceControl to clear the frame rate of.
+ * @return This transaction object.
+ *
+ * @see #setFrameRate(SurfaceControl, float, int)
+ */
+ @NonNull
+ public Transaction clearFrameRate(@NonNull SurfaceControl sc) {
+ checkPreconditions(sc);
+ nativeSetFrameRate(mNativeObject, sc.mNativeObject, 0.0f,
+ Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ Surface.CHANGE_FRAME_RATE_ALWAYS);
+ return this;
+ }
+
+ /**
* Sets the default frame rate compatibility for the surface {@link SurfaceControl}
*
* @param sc The SurfaceControl to specify the frame rate of.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b6c92e3..586c193 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -107,6 +107,14 @@
* and scaling a SurfaceView on screen will not cause rendering artifacts. Such
* artifacts may occur on previous versions of the platform when its window is
* positioned asynchronously.</p>
+ *
+ * <p class="note"><strong>Note:</strong> Starting in platform version
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, SurfaceView will support arbitrary
+ * alpha blending. Prior platform versions ignored alpha values on the SurfaceView if they were
+ * between 0 and 1. If the SurfaceView is configured with Z-above, then the alpha is applied
+ * directly to the Surface. If the SurfaceView is configured with Z-below, then the alpha is
+ * applied to the hole punch directly. Note that when using Z-below, overlapping SurfaceViews
+ * may not blend properly as a consequence of not applying alpha to the surface content directly.
*/
public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCallback {
private static final String TAG = "SurfaceView";
@@ -146,6 +154,7 @@
Paint mRoundedViewportPaint;
int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+ int mRequestedSubLayer = APPLICATION_MEDIA_SUBLAYER;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
boolean mIsCreating = false;
@@ -177,8 +186,7 @@
@UnsupportedAppUsage
int mRequestedFormat = PixelFormat.RGB_565;
- boolean mUseAlpha = false;
- float mSurfaceAlpha = 1f;
+ float mAlpha = 1f;
boolean mClipSurfaceToBounds;
int mBackgroundColor = Color.BLACK;
@@ -335,58 +343,25 @@
* @hide
*/
public void setUseAlpha() {
- if (!mUseAlpha) {
- mUseAlpha = true;
- updateSurfaceAlpha();
- }
+ // TODO(b/241474646): Remove me
+ return;
}
@Override
public void setAlpha(float alpha) {
- // Sets the opacity of the view to a value, where 0 means the view is completely transparent
- // and 1 means the view is completely opaque.
- //
- // Note: Alpha value of this view is ignored by default. To enable alpha blending, you need
- // to call setUseAlpha() as well.
- // This view doesn't support translucent opacity if the view is located z-below, since the
- // logic to punch a hole in the view hierarchy cannot handle such case. See also
- // #clearSurfaceViewPort(Canvas)
if (DEBUG) {
Log.d(TAG, System.identityHashCode(this)
- + " setAlpha: mUseAlpha = " + mUseAlpha + " alpha=" + alpha);
+ + " setAlpha: alpha=" + alpha);
}
super.setAlpha(alpha);
- updateSurfaceAlpha();
}
- private float getFixedAlpha() {
- // Compute alpha value to be set on the underlying surface.
- final float alpha = getAlpha();
- return mUseAlpha && (mSubLayer > 0 || alpha == 0f) ? alpha : 1f;
- }
-
- private void updateSurfaceAlpha() {
- if (!mUseAlpha || !mHaveFrame || mSurfaceControl == null) {
- return;
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ if (Math.round(mAlpha * 255) != alpha) {
+ updateSurface();
}
- final float viewAlpha = getAlpha();
- if (mSubLayer < 0 && 0f < viewAlpha && viewAlpha < 1f) {
- Log.w(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha:"
- + " translucent color is not supported for a surface placed z-below.");
- }
- final ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null) {
- return;
- }
- final float alpha = getFixedAlpha();
- if (alpha != mSurfaceAlpha) {
- final Transaction transaction = new Transaction();
- transaction.setAlpha(mSurfaceControl, alpha);
- viewRoot.applyTransactionOnDraw(transaction);
- damageInParent();
- mSurfaceAlpha = alpha;
- }
+ return true;
}
private void performDrawFinished() {
@@ -534,7 +509,15 @@
invalidate();
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ // SurfaceViews only alpha composite by modulating the Surface alpha for Z-above, or
+ // applying alpha on the hole punch for Z-below - no deferral to a layer is necessary.
+ return false;
+ }
+
private void clearSurfaceViewPort(Canvas canvas) {
+ final float alpha = getAlpha();
if (mCornerRadius > 0f) {
canvas.getClipBounds(mTmpRect);
if (mClipSurfaceToBounds && mClipBounds != null) {
@@ -546,10 +529,11 @@
mTmpRect.right,
mTmpRect.bottom,
mCornerRadius,
- mCornerRadius
+ mCornerRadius,
+ alpha
);
} else {
- canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f);
+ canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f, alpha);
}
}
@@ -652,10 +636,10 @@
} else {
subLayer = APPLICATION_MEDIA_SUBLAYER;
}
- if (mSubLayer == subLayer) {
+ if (mRequestedSubLayer == subLayer) {
return false;
}
- mSubLayer = subLayer;
+ mRequestedSubLayer = subLayer;
if (!allowDynamicChange) {
return false;
@@ -667,9 +651,8 @@
if (viewRoot == null) {
return true;
}
- final Transaction transaction = new SurfaceControl.Transaction();
- updateRelativeZ(transaction);
- viewRoot.applyTransactionOnDraw(transaction);
+
+ updateSurface();
invalidate();
return true;
}
@@ -722,8 +705,7 @@
}
private void releaseSurfaces(boolean releaseSurfacePackage) {
- mSurfaceAlpha = 1f;
-
+ mAlpha = 1f;
synchronized (mSurfaceControlLock) {
mSurface.destroy();
if (mBlastBufferQueue != null) {
@@ -770,7 +752,7 @@
}
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
- boolean creating, boolean sizeChanged, boolean hintChanged,
+ boolean creating, boolean sizeChanged, boolean hintChanged, boolean relativeZChanged,
Transaction surfaceUpdateTransaction) {
boolean realSizeChanged = false;
@@ -800,14 +782,20 @@
surfaceUpdateTransaction.hide(mSurfaceControl);
}
-
-
updateBackgroundVisibility(surfaceUpdateTransaction);
updateBackgroundColor(surfaceUpdateTransaction);
- if (mUseAlpha) {
- float alpha = getFixedAlpha();
+ if (isAboveParent()) {
+ float alpha = getAlpha();
surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha);
- mSurfaceAlpha = alpha;
+ }
+
+ if (relativeZChanged) {
+ if (!isAboveParent()) {
+ // If we're moving from z-above to z-below, then restore the surface alpha back to 1
+ // and let the holepunch drive visibility and blending.
+ surfaceUpdateTransaction.setAlpha(mSurfaceControl, 1.f);
+ }
+ updateRelativeZ(surfaceUpdateTransaction);
}
surfaceUpdateTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
@@ -873,6 +861,7 @@
} finally {
mSurfaceLock.unlock();
}
+
return realSizeChanged;
}
@@ -906,10 +895,10 @@
int myHeight = mRequestedHeight;
if (myHeight <= 0) myHeight = getHeight();
- final float alpha = getFixedAlpha();
+ final float alpha = getAlpha();
final boolean formatChanged = mFormat != mRequestedFormat;
final boolean visibleChanged = mVisible != mRequestedVisible;
- final boolean alphaChanged = mSurfaceAlpha != alpha;
+ final boolean alphaChanged = mAlpha != alpha;
final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
&& mRequestedVisible;
final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
@@ -921,17 +910,17 @@
|| getHeight() != mScreenRect.height();
final boolean hintChanged = (viewRoot.getBufferTransformHint() != mTransformHint)
&& mRequestedVisible;
+ final boolean relativeZChanged = mSubLayer != mRequestedSubLayer;
- if (creating || formatChanged || sizeChanged || visibleChanged ||
- (mUseAlpha && alphaChanged) || windowVisibleChanged ||
- positionChanged || layoutSizeChanged || hintChanged) {
+ if (creating || formatChanged || sizeChanged || visibleChanged
+ || alphaChanged || windowVisibleChanged || positionChanged
+ || layoutSizeChanged || hintChanged || relativeZChanged) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged
+ " visible=" + visibleChanged + " alpha=" + alphaChanged
+ " hint=" + hintChanged
- + " mUseAlpha=" + mUseAlpha
+ " visible=" + visibleChanged
+ " left=" + (mWindowSpaceLeft != mLocation[0])
+ " top=" + (mWindowSpaceTop != mLocation[1]));
@@ -943,8 +932,10 @@
mSurfaceWidth = myWidth;
mSurfaceHeight = myHeight;
mFormat = mRequestedFormat;
+ mAlpha = alpha;
mLastWindowVisibility = mWindowVisibility;
mTransformHint = viewRoot.getBufferTransformHint();
+ mSubLayer = mRequestedSubLayer;
mScreenRect.left = mWindowSpaceLeft;
mScreenRect.top = mWindowSpaceTop;
@@ -968,7 +959,7 @@
}
final boolean redrawNeeded = sizeChanged || creating || hintChanged
- || (mVisible && !mDrawFinished);
+ || (mVisible && !mDrawFinished) || alphaChanged || relativeZChanged;
boolean shouldSyncBuffer =
redrawNeeded && viewRoot.wasRelayoutRequested() && viewRoot.isInLocalSync();
SyncBufferTransactionCallback syncBufferTransactionCallback = null;
@@ -979,8 +970,9 @@
syncBufferTransactionCallback::onTransactionReady);
}
- final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
- translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction);
+ final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator,
+ creating, sizeChanged, hintChanged, relativeZChanged,
+ surfaceUpdateTransaction);
try {
SurfaceHolder.Callback[] callbacks = null;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 996dee0..3b8b479 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -151,6 +151,7 @@
import android.view.displayhash.DisplayHashResultCallback;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
import android.view.inspector.InspectableProperty.FlagEntry;
@@ -31800,6 +31801,15 @@
}
/**
+ * Return whether the stylus handwriting is available for this View.
+ * @hide
+ */
+ public boolean isStylusHandwritingAvailable() {
+ return getContext().getSystemService(InputMethodManager.class)
+ .isStylusHandwritingAvailable();
+ }
+
+ /**
* Collects a {@link ViewTranslationRequest} which represents the content to be translated in
* the view.
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e7dbae9..5729c60 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -286,7 +286,7 @@
* @hide
*/
public static final boolean LOCAL_LAYOUT =
- SystemProperties.getBoolean("persist.debug.local_layout", true);
+ SystemProperties.getBoolean("persist.debug.local_layout", false);
/**
* Set this system property to true to force the view hierarchy to render
@@ -1787,6 +1787,7 @@
final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
final boolean forceNextWindowRelayout = args.argi1 != 0;
final int displayId = args.argi3;
final int resizeMode = args.argi5;
@@ -8242,6 +8243,7 @@
mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
}
mInvSizeCompatScale = 1f / mTmpFrames.sizeCompatScale;
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mPendingMergedConfiguration);
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls);
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 2db0dcb..7d8f363b 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -1332,6 +1332,7 @@
* Convenience method to obtain a {@link #TYPE_WINDOWS_CHANGED} event for a specific window and
* change set.
*
+ * @param displayId The ID of the display from which the event comes from
* @param windowId The ID of the window that changed
* @param windowChangeTypes The changes to populate
* @return An instance of a TYPE_WINDOWS_CHANGED, populated with the requested fields and with
@@ -1340,8 +1341,9 @@
* @hide
*/
public static AccessibilityEvent obtainWindowsChangedEvent(
- int windowId, int windowChangeTypes) {
+ int displayId, int windowId, int windowChangeTypes) {
final AccessibilityEvent event = new AccessibilityEvent(TYPE_WINDOWS_CHANGED);
+ event.setDisplayId(displayId);
event.setWindowId(windowId);
event.setWindowChanges(windowChangeTypes);
event.setImportantForAccessibility(true);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 6049613..0c7c163 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -439,17 +439,20 @@
/**
* True if this input method client is active, initially false.
*/
+ @GuardedBy("mH")
private boolean mActive = false;
/**
* {@code true} if next {@link ImeFocusController#onPostWindowFocus} needs to
* restart input.
*/
+ @GuardedBy("mH")
private boolean mRestartOnNextWindowFocus = true;
/**
* As reported by IME through InputConnection.
*/
+ @GuardedBy("mH")
private boolean mFullscreenMode;
// -----------------------------------------------------------
@@ -460,46 +463,70 @@
*/
@GuardedBy("mH")
ViewRootImpl mCurRootView;
+
/**
* This is set when we are in the process of connecting, to determine
* when we have actually finished.
*/
+ @GuardedBy("mH")
private boolean mServedConnecting;
+
/**
* This is non-null when we have connected the served view; it holds
* the attributes that were last retrieved from the served view and given
* to the input connection.
*/
+ @GuardedBy("mH")
private EditorInfo mCurrentEditorInfo;
+
+ @GuardedBy("mH")
+ @Nullable
+ private ViewFocusParameterInfo mPreviousViewFocusParameters;
+
/**
* The InputConnection that was last retrieved from the served view.
*/
+ @GuardedBy("mH")
private RemoteInputConnectionImpl mServedInputConnection;
+
/**
* The completions that were last provided by the served view.
*/
+ @GuardedBy("mH")
private CompletionInfo[] mCompletions;
// Cursor position on the screen.
+ @GuardedBy("mH")
@UnsupportedAppUsage
Rect mTmpCursorRect = new Rect();
+
+ @GuardedBy("mH")
@UnsupportedAppUsage
Rect mCursorRect = new Rect();
+
+ @GuardedBy("mH")
private int mCursorSelStart;
+ @GuardedBy("mH")
private int mCursorSelEnd;
+ @GuardedBy("mH")
private int mCursorCandStart;
+ @GuardedBy("mH")
private int mCursorCandEnd;
+ @GuardedBy("mH")
private int mInitialSelStart;
+ @GuardedBy("mH")
private int mInitialSelEnd;
/**
* Handler for {@link RemoteInputConnectionImpl#getInputConnection()}.
*/
+ @GuardedBy("mH")
private Handler mServedInputConnectionHandler;
/**
* The instance that has previously been sent to the input method.
*/
+ @GuardedBy("mH")
private CursorAnchorInfo mCursorAnchorInfo = null;
/**
@@ -522,6 +549,7 @@
* @deprecated New code should use {@code mCurBindState.mImeId}.
*/
@Deprecated
+ @GuardedBy("mH")
@UnsupportedAppUsage
String mCurId;
@@ -551,7 +579,9 @@
private final SparseArray<IAccessibilityInputMethodSessionInvoker>
mAccessibilityInputMethodSession = new SparseArray<>();
+ @GuardedBy("mH")
private InputChannel mCurChannel;
+ @GuardedBy("mH")
private ImeInputEventSender mCurSender;
private static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE = 0x0;
@@ -561,14 +591,18 @@
* @deprecated This is kept for {@link UnsupportedAppUsage}. Must not be used.
*/
@Deprecated
+ @GuardedBy("mH")
private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
/**
* Applies the IME visibility and listens for other state changes.
*/
+ @GuardedBy("mH")
private ImeInsetsSourceConsumer mImeInsetsConsumer;
+ @GuardedBy("mH")
private final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
+ @GuardedBy("mH")
private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
private final DelegateImpl mDelegate = new DelegateImpl();
@@ -660,26 +694,6 @@
private final class DelegateImpl implements
ImeFocusController.InputMethodManagerDelegate {
- @GuardedBy("mH")
- @Nullable
- private ViewFocusParameterInfo mPreviousViewFocusParameters;
-
- @GuardedBy("mH")
- private void updatePreviousViewFocusParametersLocked(
- @Nullable EditorInfo currentEditorInfo,
- @StartInputFlags int startInputFlags,
- @StartInputReason int startInputReason,
- @SoftInputModeFlags int softInputMode,
- int windowFlags) {
- mPreviousViewFocusParameters = new ViewFocusParameterInfo(currentEditorInfo,
- startInputFlags, startInputReason, softInputMode, windowFlags);
- }
-
- @GuardedBy("mH")
- private void clearStateLocked() {
- mPreviousViewFocusParameters = null;
- }
-
/**
* Used by {@link ImeFocusController} to start input connection.
*/
@@ -800,8 +814,10 @@
*/
@Override
public void finishComposingText() {
- if (mServedInputConnection != null) {
- mServedInputConnection.finishComposingTextFromImm();
+ synchronized (mH) {
+ if (mServedInputConnection != null) {
+ mServedInputConnection.finishComposingTextFromImm();
+ }
}
}
@@ -833,11 +849,13 @@
*/
@Override
public boolean isRestartOnNextWindowFocus(boolean reset) {
- final boolean result = mRestartOnNextWindowFocus;
- if (reset) {
- mRestartOnNextWindowFocus = false;
+ synchronized (mH) {
+ final boolean result = mRestartOnNextWindowFocus;
+ if (reset) {
+ mRestartOnNextWindowFocus = false;
+ }
+ return result;
}
- return result;
}
/**
@@ -878,21 +896,25 @@
return mDelegate.hasActiveConnection(view);
}
+ @GuardedBy("mH")
private View getServedViewLocked() {
return mCurRootView != null ? mCurRootView.getImeFocusController().getServedView() : null;
}
+ @GuardedBy("mH")
private View getNextServedViewLocked() {
return mCurRootView != null ? mCurRootView.getImeFocusController().getNextServedView()
: null;
}
+ @GuardedBy("mH")
private void setServedViewLocked(View view) {
if (mCurRootView != null) {
mCurRootView.getImeFocusController().setServedView(view);
}
}
+ @GuardedBy("mH")
private void setNextServedViewLocked(View view) {
if (mCurRootView != null) {
mCurRootView.getImeFocusController().setNextServedView(view);
@@ -911,6 +933,7 @@
/**
* Returns {@code true} when the given view has been served by Input Method.
*/
+ @GuardedBy("mH")
private boolean hasServedByInputMethodLocked(View view) {
final View servedView = getServedViewLocked();
return (servedView == view
@@ -1729,7 +1752,7 @@
@GuardedBy("mH")
private void clearConnectionLocked() {
mCurrentEditorInfo = null;
- mDelegate.clearStateLocked();
+ mPreviousViewFocusParameters = null;
if (mServedInputConnection != null) {
mServedInputConnection.deactivate();
mServedInputConnection = null;
@@ -1748,7 +1771,7 @@
if (getServedViewLocked() != null) {
if (DEBUG) {
Log.v(TAG, "FINISH INPUT: mServedView="
- + dumpViewInfo(getServedViewLocked()));
+ + InputMethodDebug.dumpViewInfo(getServedViewLocked()));
}
setServedViewLocked(null);
mCompletions = null;
@@ -2281,7 +2304,7 @@
// Make sure we have a window token for the served view.
if (DEBUG) {
- Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) +
+ Log.v(TAG, "Starting input: view=" + InputMethodDebug.dumpViewInfo(view) +
" reason=" + InputMethodDebug.startInputReasonToString(startInputReason));
}
if (view == null) {
@@ -2352,9 +2375,9 @@
final View servedView = getServedViewLocked();
if (servedView != view || !mServedConnecting) {
// Something else happened, so abort.
- if (DEBUG) Log.v(TAG,
- "Starting input: finished by someone else. view=" + dumpViewInfo(view)
- + " servedView=" + dumpViewInfo(servedView)
+ if (DEBUG) Log.v(TAG, "Starting input: finished by someone else."
+ + " view=" + InputMethodDebug.dumpViewInfo(view)
+ + " servedView=" + InputMethodDebug.dumpViewInfo(servedView)
+ " mServedConnecting=" + mServedConnecting);
if (mServedInputConnection != null && startInputReason == BOUND_TO_IMMS) {
// This is not an error. Once IME binds (MSG_BIND), InputConnection is fully
@@ -2414,8 +2437,8 @@
mServedInputConnection = servedInputConnection;
if (DEBUG) {
- Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
- + ic + " editorInfo=" + editorInfo + " startInputFlags="
+ Log.v(TAG, "START INPUT: view=" + InputMethodDebug.dumpViewInfo(view)
+ + " ic=" + ic + " editorInfo=" + editorInfo + " startInputFlags="
+ InputMethodDebug.startInputFlagsToString(startInputFlags));
}
@@ -2424,10 +2447,10 @@
&& previouslyServedConnection == null
&& ic == null
&& isSwitchingBetweenEquivalentNonEditableViews(
- mDelegate.mPreviousViewFocusParameters, startInputFlags,
+ mPreviousViewFocusParameters, startInputFlags,
startInputReason, softInputMode, windowFlags);
- updatePreviousViewFocusParametersLocked(mCurrentEditorInfo, startInputFlags,
- startInputReason, softInputMode, windowFlags);
+ mPreviousViewFocusParameters = new ViewFocusParameterInfo(mCurrentEditorInfo,
+ startInputFlags, startInputReason, softInputMode, windowFlags);
if (canSkip) {
if (DEBUG) {
Log.d(TAG, "Not calling IMMS due to switching between non-editable views.");
@@ -2499,29 +2522,6 @@
}
/**
- * This method exists only so that the
- * <a href="https://errorprone.info/bugpattern/GuardedBy">errorprone</a> false positive warning
- * can be suppressed without granting a blanket exception to the {@link #startInputInner}
- * method.
- * <p>
- * The warning in question implies that the access to the
- * {@link DelegateImpl#updatePreviousViewFocusParametersLocked} method should be guarded by
- * {@code InputMethodManager.this.mH}, but instead {@code mDelegate.mH} is held in the caller.
- * In this case errorprone fails to realize that it is the same object.
- */
- @GuardedBy("mH")
- @SuppressWarnings("GuardedBy")
- private void updatePreviousViewFocusParametersLocked(
- @Nullable EditorInfo currentEditorInfo,
- @StartInputFlags int startInputFlags,
- @StartInputReason int startInputReason,
- @SoftInputModeFlags int softInputMode,
- int windowFlags) {
- mDelegate.updatePreviousViewFocusParametersLocked(currentEditorInfo, startInputFlags,
- startInputReason, softInputMode, windowFlags);
- }
-
- /**
* @return {@code true} when we are switching focus between two non-editable views
* so that we can avoid calling {@link IInputMethodManager#startInputOrWindowGainedFocus}.
*/
@@ -3192,6 +3192,7 @@
}
// Must be called on the main looper
+ @GuardedBy("mH")
private int sendInputEventOnMainLooperLocked(PendingEvent p) {
if (mCurChannel != null) {
if (mCurSender == null) {
@@ -3256,6 +3257,7 @@
}
}
+ @GuardedBy("mH")
private void flushPendingEventsLocked() {
mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
@@ -3268,6 +3270,7 @@
}
}
+ @GuardedBy("mH")
private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
PendingEvent p = mPendingEventPool.acquire();
@@ -3282,6 +3285,7 @@
return p;
}
+ @GuardedBy("mH")
private void recyclePendingEventLocked(PendingEvent p) {
p.recycle();
mPendingEventPool.release(p);
@@ -3314,6 +3318,7 @@
mServiceInvoker.showInputMethodPickerFromSystem(mClient, mode, displayId);
}
+ @GuardedBy("mH")
private void showInputMethodPickerLocked() {
mServiceInvoker.showInputMethodPickerFromClient(mClient, SHOW_IM_PICKER_MODE_AUTO);
}
@@ -3769,23 +3774,6 @@
return mCurBindState != null ? mCurBindState.mBindSequence : -1;
}
- private static String dumpViewInfo(@Nullable final View view) {
- if (view == null) {
- return "null";
- }
- final StringBuilder sb = new StringBuilder();
- sb.append(view);
- sb.append(",focus=" + view.hasFocus());
- sb.append(",windowFocus=" + view.hasWindowFocus());
- sb.append(",autofillUiShowing=" + isAutofillUIShowing(view));
- sb.append(",window=" + view.getWindowToken());
- sb.append(",displayId=" + view.getContext().getDisplayId());
- sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
- sb.append(",hasImeFocus=" + view.hasImeFocus());
-
- return sb.toString();
- }
-
/**
* Checks the args to see if a proto-based ime dump was requested and writes the client side
* ime dump to the given {@link FileDescriptor}.
@@ -3848,6 +3836,7 @@
}
}
+ @GuardedBy("mH")
private void forAccessibilitySessionsLocked(
Consumer<IAccessibilityInputMethodSessionInvoker> consumer) {
for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e39b7cd..18ebe30 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9309,7 +9309,7 @@
}
Selection.setSelection(getEditableText(), range[0], range[1]);
mEditor.startSelectionActionModeAsync(/* adjustSelection= */ false);
- return 0;
+ return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
}
/** @hide */
@@ -9320,8 +9320,8 @@
}
getEditableText().delete(range[0], range[1]);
Selection.setSelection(getEditableText(), range[0]);
- // TODO: Delete extra spaces.
- return 0;
+ // TODO(b/243983058): Delete extra spaces.
+ return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
}
/** @hide */
@@ -9345,16 +9345,17 @@
String textToInsert = gesture.getTextToInsert();
getEditableText().insert(offset, textToInsert);
Selection.setSelection(getEditableText(), offset + textToInsert.length());
- // TODO: Insert extra spaces if necessary.
- return 0;
+ // TODO(b/243980426): Insert extra spaces if necessary.
+ return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
}
private int handleGestureFailure(HandwritingGesture gesture) {
if (!TextUtils.isEmpty(gesture.getFallbackText())) {
getEditableText()
.replace(getSelectionStart(), getSelectionEnd(), gesture.getFallbackText());
+ return InputConnection.HANDWRITING_GESTURE_RESULT_FALLBACK;
}
- return 0;
+ return InputConnection.HANDWRITING_GESTURE_RESULT_FAILED;
}
@Nullable
@@ -11955,6 +11956,11 @@
return onTextContextMenuItem(ID_PASTE);
}
break;
+ case KeyEvent.KEYCODE_Y:
+ if (canRedo()) {
+ return onTextContextMenuItem(ID_REDO);
+ }
+ break;
}
} else if (event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)) {
// Handle Ctrl-Shift shortcuts.
@@ -12043,6 +12049,17 @@
}
}
+ /** @hide */
+ @Override
+ public boolean isStylusHandwritingAvailable() {
+ if (mTextOperationUser == null) {
+ return super.isStylusHandwritingAvailable();
+ }
+ final int userId = mTextOperationUser.getIdentifier();
+ final InputMethodManager imm = getInputMethodManager();
+ return imm.isStylusHandwritingAvailableAsUser(userId);
+ }
+
@Nullable
final TextServicesManager getTextServicesManagerForUser() {
return getServiceManagerForUser("android", TextServicesManager.class);
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index dc1f612..8ca763e 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -397,6 +397,8 @@
private @Surface.Rotation int mEndFixedRotation = ROTATION_UNDEFINED;
private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
private @ColorInt int mBackgroundColor;
+ private SurfaceControl mSnapshot = null;
+ private float mSnapshotLuma;
public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
mContainer = container;
@@ -420,6 +422,8 @@
mEndFixedRotation = in.readInt();
mRotationAnimation = in.readInt();
mBackgroundColor = in.readInt();
+ mSnapshot = in.readTypedObject(SurfaceControl.CREATOR);
+ mSnapshotLuma = in.readFloat();
}
/** Sets the parent of this change's container. The parent must be a participant or null. */
@@ -489,6 +493,12 @@
mBackgroundColor = backgroundColor;
}
+ /** Sets a snapshot surface for the "start" state of the container. */
+ public void setSnapshot(@Nullable SurfaceControl snapshot, float luma) {
+ mSnapshot = snapshot;
+ mSnapshotLuma = luma;
+ }
+
/** @return the container that is changing. May be null if non-remotable (eg. activity) */
@Nullable
public WindowContainerToken getContainer() {
@@ -587,6 +597,17 @@
return mBackgroundColor;
}
+ /** @return a snapshot surface (if applicable). */
+ @Nullable
+ public SurfaceControl getSnapshot() {
+ return mSnapshot;
+ }
+
+ /** @return the luma calculated for the snapshot surface (if applicable). */
+ public float getSnapshotLuma() {
+ return mSnapshotLuma;
+ }
+
/** @hide */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -605,6 +626,8 @@
dest.writeInt(mEndFixedRotation);
dest.writeInt(mRotationAnimation);
dest.writeInt(mBackgroundColor);
+ dest.writeTypedObject(mSnapshot, flags);
+ dest.writeFloat(mSnapshotLuma);
}
@NonNull
@@ -629,11 +652,13 @@
@Override
public String toString() {
- return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
+ String out = "{" + mContainer + "(" + mParent + ") leash=" + mLeash
+ " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
+ mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
+ mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation
- + " endFixedRotation=" + mEndFixedRotation + "}";
+ + " endFixedRotation=" + mEndFixedRotation;
+ if (mSnapshot != null) out += " snapshot=" + mSnapshot;
+ return out + "}";
}
}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 0976f45..a208634 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -28,6 +28,7 @@
import android.app.IWindowToken;
import android.app.ResourcesManager;
import android.content.Context;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.Build;
@@ -221,6 +222,7 @@
if (context == null) {
return;
}
+ CompatibilityInfo.applyOverrideScaleIfNeeded(newConfig);
final boolean displayChanged;
final boolean shouldUpdateResources;
final int diff;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f6c64732..1bc934d 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -63,6 +63,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -146,6 +147,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.function.Supplier;
/**
@@ -281,6 +283,7 @@
private long mQueriedSharingShortcutsTimeMs;
private int mCurrAvailableWidth = 0;
+ private Insets mLastAppliedInsets = null;
private int mLastNumberOfChildren = -1;
private int mMaxTargetsPerRow = 1;
@@ -1632,6 +1635,8 @@
if (mChooserMultiProfilePagerAdapter.getInactiveListAdapter() != null) {
mChooserMultiProfilePagerAdapter.getInactiveListAdapter().destroyAppPredictor();
}
+ mPersonalAppPredictor = null;
+ mWorkAppPredictor = null;
}
@Override // ResolverListCommunicator
@@ -2544,7 +2549,11 @@
|| gridAdapter.calculateChooserTargetWidth(availableWidth)
|| recyclerView.getAdapter() == null
|| availableWidth != mCurrAvailableWidth;
+
+ boolean insetsChanged = !Objects.equals(mLastAppliedInsets, mSystemWindowInsets);
+
if (isLayoutUpdated
+ || insetsChanged
|| mLastNumberOfChildren != recyclerView.getChildCount()) {
mCurrAvailableWidth = availableWidth;
if (isLayoutUpdated) {
@@ -2565,7 +2574,7 @@
return;
}
- if (mLastNumberOfChildren == recyclerView.getChildCount()) {
+ if (mLastNumberOfChildren == recyclerView.getChildCount() && !insetsChanged) {
return;
}
@@ -2576,6 +2585,7 @@
int offset = calculateDrawerOffset(top, bottom, recyclerView, gridAdapter);
mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
mEnterTransitionAnimationDelegate.markOffsetCalculated();
+ mLastAppliedInsets = mSystemWindowInsets;
});
}
}
@@ -3068,7 +3078,12 @@
mChooserMultiProfilePagerAdapter.setupContainerPadding(
getActiveEmptyStateView().findViewById(R.id.resolver_empty_state_container));
}
- return super.onApplyWindowInsets(v, insets);
+
+ WindowInsets result = super.onApplyWindowInsets(v, insets);
+ if (mResolverDrawerLayout != null) {
+ mResolverDrawerLayout.requestLayout();
+ }
+ return result;
}
private void setHorizontalScrollingEnabled(boolean enabled) {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 1ec5325..e57b90a 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -43,6 +43,7 @@
import android.widget.TextView;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.ChooserTargetInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
@@ -86,7 +87,7 @@
private final ChooserActivityLogger mChooserActivityLogger;
private int mNumShortcutResults = 0;
- private Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>();
+ private final Map<TargetInfo, AsyncTask> mIconLoaders = new HashMap<>();
private boolean mApplySharingAppLimits;
// Reserve spots for incoming direct share targets by adding placeholders
@@ -104,6 +105,8 @@
private AppPredictor mAppPredictor;
private AppPredictor.Callback mAppPredictorCallback;
+ private LoadDirectShareIconTaskProvider mTestLoadDirectShareTaskProvider;
+
// For pinned direct share labels, if the text spans multiple lines, the TextView will consume
// the full width, even if the characters actually take up less than that. Measure the actual
// line widths and constrain the View's width based upon that so that the pin doesn't end up
@@ -240,7 +243,6 @@
mListViewDataChanged = false;
}
-
private void createPlaceHolders() {
mNumShortcutResults = 0;
mServiceTargets.clear();
@@ -265,31 +267,25 @@
return;
}
- if (!(info instanceof DisplayResolveInfo)) {
- holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
- holder.bindIcon(info);
-
- if (info instanceof SelectableTargetInfo) {
- // direct share targets should append the application name for a better readout
- DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
- CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
- CharSequence extendedInfo = info.getExtendedInfo();
- String contentDescription = String.join(" ", info.getDisplayLabel(),
- extendedInfo != null ? extendedInfo : "", appName);
- holder.updateContentDescription(contentDescription);
- }
- } else {
+ if (info instanceof DisplayResolveInfo) {
DisplayResolveInfo dri = (DisplayResolveInfo) info;
holder.bindLabel(dri.getDisplayLabel(), dri.getExtendedInfo(), alwaysShowSubLabel());
- LoadIconTask task = mIconLoaders.get(dri);
- if (task == null) {
- task = new LoadIconTask(dri, holder);
- mIconLoaders.put(dri, task);
- task.execute();
+ startDisplayResolveInfoIconLoading(holder, dri);
+ } else {
+ holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
+
+ if (info instanceof SelectableTargetInfo) {
+ SelectableTargetInfo selectableInfo = (SelectableTargetInfo) info;
+ // direct share targets should append the application name for a better readout
+ DisplayResolveInfo rInfo = selectableInfo.getDisplayResolveInfo();
+ CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
+ CharSequence extendedInfo = selectableInfo.getExtendedInfo();
+ String contentDescription = String.join(" ", selectableInfo.getDisplayLabel(),
+ extendedInfo != null ? extendedInfo : "", appName);
+ holder.updateContentDescription(contentDescription);
+ startSelectableTargetInfoIconLoading(holder, selectableInfo);
} else {
- // The holder was potentially changed as the underlying items were
- // reshuffled, so reset the target holder
- task.setViewHolder(holder);
+ holder.bindIcon(info);
}
}
@@ -330,6 +326,32 @@
}
}
+ private void startDisplayResolveInfoIconLoading(ViewHolder holder, DisplayResolveInfo info) {
+ LoadIconTask task = (LoadIconTask) mIconLoaders.get(info);
+ if (task == null) {
+ task = new LoadIconTask(info, holder);
+ mIconLoaders.put(info, task);
+ task.execute();
+ } else {
+ // The holder was potentially changed as the underlying items were
+ // reshuffled, so reset the target holder
+ task.setViewHolder(holder);
+ }
+ }
+
+ private void startSelectableTargetInfoIconLoading(
+ ViewHolder holder, SelectableTargetInfo info) {
+ LoadDirectShareIconTask task = (LoadDirectShareIconTask) mIconLoaders.get(info);
+ if (task == null) {
+ task = mTestLoadDirectShareTaskProvider == null
+ ? new LoadDirectShareIconTask(info)
+ : mTestLoadDirectShareTaskProvider.get();
+ mIconLoaders.put(info, task);
+ task.loadIcon();
+ }
+ task.setViewHolder(holder);
+ }
+
void updateAlphabeticalList() {
new AsyncTask<Void, Void, List<DisplayResolveInfo>>() {
@Override
@@ -344,7 +366,7 @@
Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
for (DisplayResolveInfo info : allTargets) {
String resolvedTarget = info.getResolvedComponentName().getPackageName()
- + '#' + info.getDisplayLabel();
+ + '#' + info.getDisplayLabel();
DisplayResolveInfo multiDri = consolidated.get(resolvedTarget);
if (multiDri == null) {
consolidated.put(resolvedTarget, info);
@@ -353,7 +375,7 @@
} else {
// create consolidated target from the single DisplayResolveInfo
MultiDisplayResolveInfo multiDisplayResolveInfo =
- new MultiDisplayResolveInfo(resolvedTarget, multiDri);
+ new MultiDisplayResolveInfo(resolvedTarget, multiDri);
multiDisplayResolveInfo.addTarget(info);
consolidated.put(resolvedTarget, multiDisplayResolveInfo);
}
@@ -740,10 +762,24 @@
}
/**
+ * An alias for onBindView to use with unit tests.
+ */
+ @VisibleForTesting
+ public void testViewBind(View view, TargetInfo info, int position) {
+ onBindView(view, info, position);
+ }
+
+ @VisibleForTesting
+ public void setTestLoadDirectShareTaskProvider(LoadDirectShareIconTaskProvider provider) {
+ mTestLoadDirectShareTaskProvider = provider;
+ }
+
+ /**
* Necessary methods to communicate between {@link ChooserListAdapter}
* and {@link ChooserActivity}.
*/
- interface ChooserListCommunicator extends ResolverListCommunicator {
+ @VisibleForTesting
+ public interface ChooserListCommunicator extends ResolverListCommunicator {
int getMaxRankedTargets();
@@ -751,4 +787,59 @@
boolean isSendAction(Intent targetIntent);
}
+
+ /**
+ * Loads direct share targets icons.
+ */
+ @VisibleForTesting
+ public class LoadDirectShareIconTask extends AsyncTask<Void, Void, Void> {
+ private final SelectableTargetInfo mTargetInfo;
+ private ViewHolder mViewHolder;
+
+ private LoadDirectShareIconTask(SelectableTargetInfo targetInfo) {
+ mTargetInfo = targetInfo;
+ }
+
+ @Override
+ protected Void doInBackground(Void... voids) {
+ mTargetInfo.loadIcon();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void arg) {
+ if (mViewHolder != null) {
+ mViewHolder.bindIcon(mTargetInfo);
+ notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Specifies a view holder that will be updated when the task is completed.
+ */
+ public void setViewHolder(ViewHolder viewHolder) {
+ mViewHolder = viewHolder;
+ mViewHolder.bindIcon(mTargetInfo);
+ notifyDataSetChanged();
+ }
+
+ /**
+ * An alias for execute to use with unit tests.
+ */
+ public void loadIcon() {
+ execute();
+ }
+ }
+
+ /**
+ * An interface for the unit tests to override icon loading task creation
+ */
+ @VisibleForTesting
+ public interface LoadDirectShareIconTaskProvider {
+ /**
+ * Provides an instance of the task.
+ * @return
+ */
+ LoadDirectShareIconTask get();
+ }
}
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 66fff5c..3a3baa7 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -834,7 +834,11 @@
void onHandlePackagesChanged(ResolverListAdapter listAdapter);
}
- static class ViewHolder {
+ /**
+ * A view holder.
+ */
+ @VisibleForTesting
+ public static class ViewHolder {
public View itemView;
public Drawable defaultItemViewBackground;
@@ -842,7 +846,8 @@
public TextView text2;
public ImageView icon;
- ViewHolder(View view) {
+ @VisibleForTesting
+ public ViewHolder(View view) {
itemView = view;
defaultItemViewBackground = view.getBackground();
text = (TextView) view.findViewById(com.android.internal.R.id.text1);
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 264e4f7..37eab40 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -37,6 +37,7 @@
import android.text.SpannableStringBuilder;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ChooserActivity;
import com.android.internal.app.ResolverActivity;
import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
@@ -59,8 +60,11 @@
private final String mDisplayLabel;
private final PackageManager mPm;
private final SelectableTargetInfoCommunicator mSelectableTargetInfoCommunicator;
+ @GuardedBy("this")
+ private ShortcutInfo mShortcutInfo;
private Drawable mBadgeIcon = null;
private CharSequence mBadgeContentDescription;
+ @GuardedBy("this")
private Drawable mDisplayIcon;
private final Intent mFillInIntent;
private final int mFillInFlags;
@@ -78,6 +82,7 @@
mModifiedScore = modifiedScore;
mPm = mContext.getPackageManager();
mSelectableTargetInfoCommunicator = selectableTargetInfoComunicator;
+ mShortcutInfo = shortcutInfo;
mIsPinned = shortcutInfo != null && shortcutInfo.isPinned();
if (sourceInfo != null) {
final ResolveInfo ri = sourceInfo.getResolveInfo();
@@ -92,8 +97,6 @@
}
}
}
- // TODO(b/121287224): do this in the background thread, and only for selected targets
- mDisplayIcon = getChooserTargetIconDrawable(chooserTarget, shortcutInfo);
if (sourceInfo != null) {
mBackupResolveInfo = null;
@@ -118,7 +121,10 @@
mChooserTarget = other.mChooserTarget;
mBadgeIcon = other.mBadgeIcon;
mBadgeContentDescription = other.mBadgeContentDescription;
- mDisplayIcon = other.mDisplayIcon;
+ synchronized (other) {
+ mShortcutInfo = other.mShortcutInfo;
+ mDisplayIcon = other.mDisplayIcon;
+ }
mFillInIntent = fillInIntent;
mFillInFlags = flags;
mModifiedScore = other.mModifiedScore;
@@ -141,6 +147,25 @@
return mSourceInfo;
}
+ /**
+ * Load display icon, if needed.
+ */
+ public void loadIcon() {
+ ShortcutInfo shortcutInfo;
+ Drawable icon;
+ synchronized (this) {
+ shortcutInfo = mShortcutInfo;
+ icon = mDisplayIcon;
+ }
+ if (icon == null && shortcutInfo != null) {
+ icon = getChooserTargetIconDrawable(mChooserTarget, shortcutInfo);
+ synchronized (this) {
+ mDisplayIcon = icon;
+ mShortcutInfo = null;
+ }
+ }
+ }
+
private Drawable getChooserTargetIconDrawable(ChooserTarget target,
@Nullable ShortcutInfo shortcutInfo) {
Drawable directShareIcon = null;
@@ -270,7 +295,7 @@
}
@Override
- public Drawable getDisplayIcon(Context context) {
+ public synchronized Drawable getDisplayIcon(Context context) {
return mDisplayIcon;
}
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 9211926..1e52087 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -278,7 +278,7 @@
} else if (gesture instanceof InsertGesture) {
result = mTextView.performHandwritingInsertGesture((InsertGesture) gesture);
} else {
- result = 0;
+ result = HANDWRITING_GESTURE_RESULT_UNSUPPORTED;
}
if (executor != null && consumer != null) {
executor.execute(() -> consumer.accept(result));
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index b5fc05b..1852c59 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -16,10 +16,11 @@
package com.android.internal.inputmethod;
-import android.annotation.AnyThread;
-import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.autofill.AutofillManager;
import android.view.inputmethod.HandwritingGesture;
import java.util.StringJoiner;
@@ -281,20 +282,21 @@
}
/**
- * Return a fixed size string of the object.
- * TODO(b/151575861): Take & return with StringBuilder to make more memory efficient.
+ * Dumps the given {@link View} related to input method focus state for debugging.
*/
- @NonNull
- @AnyThread
- public static String objToString(Object obj) {
- if (obj == null) {
+ public static String dumpViewInfo(@Nullable View view) {
+ if (view == null) {
return "null";
}
- StringBuilder sb = new StringBuilder(64);
- sb.setLength(0);
- sb.append(obj.getClass().getName());
- sb.append("@");
- sb.append(Integer.toHexString(obj.hashCode()));
+ final StringBuilder sb = new StringBuilder();
+ sb.append(view);
+ sb.append(",focus=" + view.hasFocus());
+ sb.append(",windowFocus=" + view.hasWindowFocus());
+ sb.append(",window=" + view.getWindowToken());
+ sb.append(",displayId=" + view.getContext().getDisplayId());
+ sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
+ sb.append(",hasImeFocus=" + view.hasImeFocus());
+
return sb.toString();
}
}
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 527286cf0..a065e2b 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -287,7 +287,6 @@
int dividerMax = isHorizontalDivision
? mDisplayHeight
: mDisplayWidth;
- int navBarSize = isHorizontalDivision ? mInsets.bottom : mInsets.right;
int startPos = -mDividerSize;
if (dockedSide == DOCKED_RIGHT) {
startPos += mInsets.left;
@@ -308,8 +307,7 @@
addMinimizedTarget(isHorizontalDivision, dockedSide);
break;
}
- mTargets.add(new SnapTarget(dividerMax - navBarSize, dividerMax,
- SnapTarget.FLAG_DISMISS_END, 0.35f));
+ mTargets.add(new SnapTarget(dividerMax, dividerMax, SnapTarget.FLAG_DISMISS_END, 0.35f));
}
private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 65bec0e..44cfe1a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -264,11 +264,6 @@
void stopTracing();
/**
- * Handles a logging command from the WM shell command.
- */
- void handleWindowManagerLoggingCommand(in String[] args, in ParcelFileDescriptor outFd);
-
- /**
* If true, suppresses the ambient display from showing. If false, re-enables the ambient
* display.
*/
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 505ef30..537efc0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -243,6 +243,7 @@
optional bool is_root_display_area = 5;
optional int32 feature_id = 6;
optional bool is_organized = 7;
+ optional bool is_ignoring_orientation_request = 8;
}
/* represents a generic child of a DisplayArea */
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 56b8260..99e98f2d 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukhandeling is gekanselleer."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukhandeling is deur gebruiker gekanselleer."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Te veel pogings. Probeer later weer."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Te veel pogings. Vingerafdruksensor is gedeaktiveer."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probeer weer."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukke is geregistreer nie."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"nie gemerk nie"</string>
<string name="selected" msgid="6614607926197755875">"gekies"</string>
<string name="not_selected" msgid="410652016565864475">"nie gekies nie"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Een ster uit {max}}other{# sterre uit {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"aan die gang"</string>
<string name="whichApplication" msgid="5432266899591255759">"Voltooi handeling met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Voltooi handeling met gebruik van %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Streekvoorkeur"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Voer taalnaam in"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Voorgestel"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Voorgestel"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Voorgestelde tale"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Voorgestelde streke"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Alle tale"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Gee <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> toegang tot alle toestelloglêers?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Gee eenmalige toegang"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Moenie toelaat nie"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Toestelloglêers teken aan wat op jou toestel gebeur. Programme kan hierdie loglêers gebruik om kwessies op te spoor en reg te stel.\n\nSommige loglêers bevat dalk sensitiewe inligting en daarom moet jy toegang tot alle toestelloglêers net gee aan programme wat jy vertrou. \n\nAs jy nie vir hierdie program toegang tot alle toestelloglêers gee nie, het die program steeds toegang tot eie loglêers. Jou toestelvervaardiger het dalk steeds toegang tot sommige loglêers of inligting op jou toestel."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Moenie weer wys nie"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wil <xliff:g id="APP_2">%2$s</xliff:g>-skyfies wys"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Wysig"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a68c514..efb6686 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"የጣት አሻራ ስርዓተ ክወና ተትቷል።"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል።"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"በጣም ብዙ ሙከራዎች። የጣት አሻራ ዳሳሽ ተሰናክሏል።"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"እንደገና ይሞክሩ።"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ምንም የጣት አሻራዎች አልተመዘገቡም።"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ምልክት አልተደረገበትም"</string>
<string name="selected" msgid="6614607926197755875">"ተመርጧል"</string>
<string name="not_selected" msgid="410652016565864475">"አልተመረጠም"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{አንድ ኮከብ ከ{max}}one{# ኮከብ ከ{max}}other{# ኮከቦች ከ{max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"በሂደት ላይ"</string>
<string name="whichApplication" msgid="5432266899591255759">"... በመጠቀም ድርጊቱን አጠናቅ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sን ተጠቅመው እርምጃ ያጠናቅቁ"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"የክልል ምርጫ"</string>
<string name="search_language_hint" msgid="7004225294308793583">"የቋንቋ ስም ይተይቡ"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"የተጠቆሙ"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"በአስተያየት የተጠቆሙ"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"በአስተያየት የተጠቆሙ ቋንቋዎች"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"በአስተያየት የተጠቆሙ ክልሎች"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ሁሉም ቋንቋዎች"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርስ ይፈቀድለት?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"የአንድ ጊዜ መዳረሻን ፍቀድ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"አትፍቀድ"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"የመሣሪያ ምዝግብ ማስታወሻዎች በመሣሪያዎ ላይ ምን እንደሚከሰት ይመዘግባሉ። መተግበሪያዎች ችግሮችን ለማግኘት እና ለማስተካከል እነዚህን ምዝግብ ማስታወሻዎች መጠቀም ይችላሉ።\n\nአንዳንድ ምዝግብ ማስታወሻዎች ሚስጥራዊነት ያለው መረጃ ሊይዙ ይችላሉ፣ ስለዚህ የሚያምኗቸውን መተግበሪያዎች ብቻ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርሱ ይፍቀዱላቸው። \n\nይህ መተግበሪያ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርስ ካልፈቀዱለት አሁንም የራሱን ምዝግብ ማስታወሻዎች መድረስ ይችላል። የእርስዎ መሣሪያ አምራች አሁንም አንዳንድ ምዝግብ ማስታወሻዎችን ወይም መረጃዎችን በመሣሪያዎ ላይ ሊደርስ ይችላል።"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"ዳግም አታሳይ"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> የ<xliff:g id="APP_2">%2$s</xliff:g> ቁራጮችን ማሳየት ይፈልጋል"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"አርትዕ"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ንቁ መተግበሪያዎችን ይፈትሹ"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"የስልኩን ካሜራ ከእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> መድረስ አይቻልም"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ጡባዊውን ካሜራ ከእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> መድረስ አይቻልም"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"ዥረት በመልቀቅ ላይ ሳለ ይህ ሊደረስበት አይችልም። በምትኩ በስልክዎ ላይ ይሞክሩ።"</string>
<string name="system_locale_title" msgid="711882686834677268">"የሥርዓት ነባሪ"</string>
<string name="default_card_name" msgid="9198284935962911468">"ካርድ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1ede739..503b697 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -609,7 +609,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"تم إلغاء تشغيل بصمة الإصبع."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"تم إلغاء تشغيل بصمة الإصبع بواسطة المستخدم."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"تم إجراء عدد كبير من المحاولات. أعد المحاولة لاحقًا."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"تم إجراء عدد كبير جدًا من المحاولات. لذا تم إيقاف جهاز استشعار بصمات الإصبع."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"أعد المحاولة."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ليست هناك بصمات إصبع مسجَّلة."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string>
@@ -1171,8 +1172,7 @@
<string name="not_checked" msgid="7972320087569023342">"لم يتم وضع علامة"</string>
<string name="selected" msgid="6614607926197755875">"محدّد"</string>
<string name="not_selected" msgid="410652016565864475">"غير محدّد"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{نجمة واحدة من {max}}zero{# نجمة من {max}}two{نجمتان من {max}}few{# نجوم من {max}}many{# نجمة من {max}}other{# نجمة من {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"قيد التقدّم"</string>
<string name="whichApplication" msgid="5432266899591255759">"إكمال الإجراء باستخدام"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"إكمال الإجراء باستخدام %1$s"</string>
@@ -1426,7 +1426,7 @@
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"جارٍ إخراج <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"عدم الإزالة"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"إعداد"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"إلغاء"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"إخراج"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"استكشاف"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"تبديل جهاز إخراج الصوت"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> مفقود"</string>
@@ -1929,8 +1929,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"تفضيل المنطقة"</string>
<string name="search_language_hint" msgid="7004225294308793583">"اكتب اسم اللغة"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"اللغات المقترَحة"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"الاقتراحات"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"اللغات المُقترَحة"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"المناطق المُقترَحة"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"جميع اللغات"</string>
@@ -2055,8 +2054,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"هل تريد السماح لتطبيق <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> بالوصول إلى جميع سجلّات الجهاز؟"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"السماح بالوصول إلى السجلّ لمرة واحدة"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"عدم السماح"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"ترصد سجلّات الجهاز ما يحدث على جهازك. يمكن أن تستخدم التطبيقات هذه السجلّات لتحديد المشاكل وحلها.\n\nقد تحتوي بعض السجلّات على معلومات حساسة، ولذلك يجب عدم السماح بالوصول إلى جميع سجلّات الجهاز إلا للتطبيقات التي تثق بها. \n\nإذا لم تسمح بوصول هذا التطبيق إلى جميع سجلّات الجهاز، يظل بإمكان التطبيق الوصول إلى سجلّاته. ويظل بإمكان الشركة المصنِّعة لجهازك الوصول إلى بعض السجلّات أو المعلومات المتوفّرة على جهازك."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"عدم الإظهار مرة أخرى"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"يريد تطبيق <xliff:g id="APP_0">%1$s</xliff:g> عرض شرائح تطبيق <xliff:g id="APP_2">%2$s</xliff:g>."</string>
<string name="screenshot_edit" msgid="7408934887203689207">"تعديل"</string>
@@ -2297,8 +2295,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"التحقّق من التطبيقات النشطة"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"يتعذّر الوصول إلى كاميرا الهاتف من على جهاز <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"يتعذّر الوصول إلى كاميرا الجهاز اللوحي من على جهاز <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"لا يمكن الوصول إلى هذا المحتوى أثناء البث. بدلاً من ذلك، جرِّب استخدام هاتفك."</string>
<string name="system_locale_title" msgid="711882686834677268">"الإعداد التلقائي للنظام"</string>
<string name="default_card_name" msgid="9198284935962911468">"رقم البطاقة <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index edc0979..ce46419 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ’ল।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্ট ক্ৰিয়া বাতিল কৰিছে।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"অত্যধিক প্ৰয়াস। ফিংগাৰপ্ৰিণ্ট ছেন্সৰ অক্ষম কৰা হ’ল।"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"আকৌ চেষ্টা কৰক।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"টিক চিহ্ন দিয়া হোৱা নাই"</string>
<string name="selected" msgid="6614607926197755875">"বাছনি কৰা"</string>
<string name="not_selected" msgid="410652016565864475">"বাছনি কৰা হোৱা নাই"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} টাৰ ভিতৰত ১ টা তৰা}one{{max} টাৰ ভিতৰত # টা তৰা}other{{max} টাৰ ভিতৰত # টা তৰা}}"</string>
<string name="in_progress" msgid="2149208189184319441">"চলি আছে"</string>
<string name="whichApplication" msgid="5432266899591255759">"এয়া ব্যৱহাৰ কৰি কার্য সম্পূর্ণ কৰক"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যৱহাৰ কৰি কাৰ্যটো সম্পূৰ্ণ কৰক"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"অঞ্চলৰ অগ্ৰাধিকাৰ"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ভাষাৰ নাম লিখক"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"প্ৰস্তাৱিত"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"পৰামৰ্শিত"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"পৰামৰ্শিত ভাষাসমূহ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"চুপাৰিছ কৰা অঞ্চলসমূহ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"সকলো ভাষা"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>ক আটাইবোৰ ডিভাইচৰ লগ এক্সেছ কৰাৰ অনুমতি প্ৰদান কৰিবনে?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"কেৱল এবাৰ এক্সেছ কৰাৰ অনুমতি দিয়ক"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"অনুমতি নিদিব"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"আপোনাৰ ডিভাইচত কি কি ঘটে সেয়া ডিভাইচ লগে ৰেকৰ্ড কৰে। এপ্সমূহে সমস্যা বিচাৰিবলৈ আৰু সমাধান কৰিবলৈ এই লগসমূহ ব্যৱহাৰ কৰিব পাৰে।\n\nকিছুমান লগত সংবেদনশীল তথ্য থাকিব পাৰে, গতিকে কেৱল আপুনি বিশ্বাস কৰা এপকহে আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি দিয়ক। \n\nআপুনি যদি এই এপ্টোক আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি নিদিয়ে, তথাপিও ই নিজৰ লগসমূহ এক্সেছ কৰিব পাৰিব। আপোনাৰ ডিভাইচৰ নিৰ্মাতাই তথাপিও হয়তো আপোনাৰ ডিভাইচটোত থকা কিছু লগ অথবা তথ্য এক্সেছ কৰিব পাৰিব।"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"পুনৰ নেদেখুৱাব"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>এ <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাব খুজিছে"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"সম্পাদনা কৰক"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"সক্ৰিয় এপ্সমূহ পৰীক্ষা কৰক"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ৰ পৰা ফ’নটোৰ কেমেৰা এক্সেছ কৰিব নোৱাৰি"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ৰ পৰা টেবলেটটোৰ কেমেৰা এক্সেছ কৰিব নোৱাৰি"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"ষ্ট্ৰীম কৰি থকাৰ সময়ত এইটো এক্সেছ কৰিব নোৱাৰি। তাৰ পৰিৱৰ্তে আপোনাৰ ফ’নত চেষ্টা কৰি চাওক।"</string>
<string name="system_locale_title" msgid="711882686834677268">"ছিষ্টেম ডিফ’ল্ট"</string>
<string name="default_card_name" msgid="9198284935962911468">"কাৰ্ড <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 04e7024..0e2fd3f 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmaq izi əməliyyatı ləğv edildi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmaq izi əməliyyatı istifadəçi tərəfindən ləğv edildi."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Cəhdlər çox oldu. Sonraya saxlayın."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Həddindən çox cəhd. Barmaq izi sensoru deaktiv edilib."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Yenidən cəhd edin."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Barmaq izi qeydə alınmayıb."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"yoxlanılmayıb"</string>
<string name="selected" msgid="6614607926197755875">"seçilib"</string>
<string name="not_selected" msgid="410652016565864475">"seçilməyib"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1/{max} ulduz}other{#/{max} ulduz}}"</string>
<string name="in_progress" msgid="2149208189184319441">"davam edir"</string>
<string name="whichApplication" msgid="5432266899591255759">"Əməliyyatı tamamlayın:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s istifadə edərək əməliyyatı tamamlayın"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Region seçimi"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Dil adını daxil edin"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Təklif edilmiş"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Təklif edilib"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Təklif edilən dillər"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Təklif edilən regionlar"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Bütün dillər"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> tətbiqinin bütün cihaz qeydlərinə girişinə icazə verilsin?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Birdəfəlik girişə icazə verin"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"İcazə verməyin"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Cihaz qeydləri cihazınızda baş verənləri qeyd edir. Tətbiqlər problemləri tapmaq və həll etmək üçün bu qeydlərdən istifadə edə bilər.\n\nBəzi qeydlərdə həssas məlumatlar ola bilər, ona görə də yalnız etibar etdiyiniz tətbiqlərin bütün cihaz qeydlərinə giriş etməsinə icazə verin. \n\nBu tətbiqin bütün cihaz qeydlərinə girişinə icazə verməsəniz, o, hələ də öz qeydlərinə giriş edə bilər. Cihaz istehsalçınız hələ də cihazınızda bəzi qeydlərə və ya məlumatlara giriş edə bilər."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Daha göstərməyin"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> tətbiqindən bölmələr göstərmək istəyir"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Redaktə edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ba07550..05d100b 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -606,7 +606,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja sa otiskom prsta je otkazana."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju sa otiskom prsta."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Probajte ponovo kasnije."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Previše pokušaja. Senzor za otisak prsta je onemogućen."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registrovan nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
@@ -1168,8 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
<string name="selected" msgid="6614607926197755875">"izabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije izabrano"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Jedna zvezdica od {max}}one{# zvezdica od {max}}few{# zvezdice od {max}}other{# zvezdica od {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"u toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
@@ -1926,8 +1926,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Podešavanje regiona"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Unesite naziv jezika"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Predloženi"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Predloženo"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Predloženi jezici"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Predloženi regioni"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Svi jezici"</string>
@@ -2052,8 +2051,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Želite da dozvolite aplikaciji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> da pristupa svim evidencijama uređaja?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Dozvoli jednokratan pristup"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne dozvoli"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Evidencije uređaja registruju šta se dešava na uređaju. Aplikacije mogu da koriste te evidencije da bi pronašle i rešile probleme.\n\nNeke evidencije mogu da sadrže osetljive informacije, pa pristup svim evidencijama uređaja treba da dozvoljavate samo aplikacijama u koje imate poverenja. \n\nAko ne dozvolite ovoj aplikaciji da pristupa svim evidencijama uređaja, ona i dalje može da pristupa sopstvenim evidencijama. Proizvođač uređaja će možda i dalje moći da pristupa nekim evidencijama ili informacijama na uređaju."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikazuj ponovo"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi da prikazuje isečke iz aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Izmeni"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index bf8e85f..511dfdc 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Аперацыя з адбіткамі пальцаў скасавана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Аўтэнтыфікацыя па адбітках пальцаў скасавана карыстальнікам."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Занадта шмат спроб. Паспрабуйце яшчэ раз пазней."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Занадта шмат спроб. Сканер адбіткаў пальцаў выключаны."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Паўтарыце спробу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Адбіткі пальцаў не зарэгістраваны."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"не пазначана"</string>
<string name="selected" msgid="6614607926197755875">"выбраны"</string>
<string name="not_selected" msgid="410652016565864475">"не выбраны"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Адна зорка з {max}}one{# зорка з {max}}few{# зоркі з {max}}many{# зорак з {max}}other{# зоркі з {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"выконваецца"</string>
<string name="whichApplication" msgid="5432266899591255759">"Завяршыць дзеянне з дапамогай"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завяршыць дзеянне з дапамогай %1$s"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Параметры рэгіёна"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Увядзіце назву мовы"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Прапанаваныя"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Прапанавана"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Прапанаваныя мовы"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Прапанаваныя рэгіёны"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Усе мовы"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Дазволіць праграме \"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>\" мець доступ да ўсіх журналаў прылады?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Дазволіць аднаразовы доступ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дазваляць"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Журналы прылад запісваюць усё, што адбываецца на вашай прыладзе. Праграмы выкарыстоўваюць гэтыя журналы для пошуку і выпраўлення памылак.\n\nУ некаторых журналах можа ўтрымлівацца канфідэнцыяльная інфармацыя, таму давайце доступ да ўсіх журналаў прылады толькі тым праграмам, якім вы давяраеце. \n\nКалі вы не дасце гэтай праграме доступу да ўсіх журналаў прылад, у яе ўсё роўна застанецца доступ да ўласных журналаў. Для вытворцы вашай прылады будуць даступнымі некаторыя журналы і інфармацыя на вашай прыладзе."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Больш не паказваць"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Праграма <xliff:g id="APP_0">%1$s</xliff:g> запытвае дазвол на паказ зрэзаў праграмы <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Рэдагаваць"</string>
@@ -2295,8 +2293,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Праверце актыўныя праграмы"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не ўдалося атрымаць доступ да камеры тэлефона з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не ўдалося атрымаць доступ да камеры планшэта з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Не ўдаецца атрымаць доступ у час перадачы плынню. Паспрабуйце скарыстаць тэлефон."</string>
<string name="system_locale_title" msgid="711882686834677268">"Стандартная сістэмная налада"</string>
<string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 3ad22fa..8f5aa43c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцията за отпечатък е анулирана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операцията за удостоверяване чрез отпечатък бе анулирана от потребителя."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Твърде много опити. Пробвайте отново по-късно."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Твърде много опити. Сензорът за отпечатъци е деактивиран."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Опитайте отново."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Няма регистрирани отпечатъци."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"без отметка"</string>
<string name="selected" msgid="6614607926197755875">"избрано"</string>
<string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Една от {max} звезди}other{# от {max} звезди}}"</string>
<string name="in_progress" msgid="2149208189184319441">"в ход"</string>
<string name="whichApplication" msgid="5432266899591255759">"Изпълняване на действието чрез"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завършване на действието посредством %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Предпочитание за региона"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Въведете име на език"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Предложени"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Предложени"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Предложени езици"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Предложени региони"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Всички езици"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Да се разреши ли на <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> достъп до всички регистрационни файлове за устройството?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Разрешаване на еднократен достъп"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Забраняване"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"В регистрационните файлове за устройството се записва какво се извършва на него. Приложенията могат да използват тези регистрационни файлове, за да откриват и отстраняват проблеми.\n\nНякои регистрационни файлове за устройството може да съдържат поверителна информация, затова разрешавайте достъп до всички тях само на приложения, на които имате доверие. \n\nАко не разрешите на това приложение достъп до всички регистрационни файлове за устройството, то пак може да осъществява достъп до собствените си регистрационни файлове. Производителят на устройството пак може да има достъп до някои регистрационни файлове или информация на устройството ви."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Да не се показва пак"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> иска да показва части от <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Редактиране"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 8a3a3e4..7beea9a 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"আঙ্গুলের ছাপ অপারেশন বাতিল করা হয়েছে৷"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যবহারকারী আঙ্গুলের ছাপের অপারেশনটি বাতিল করেছেন।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"অনেকবার প্রচেষ্টা করা হয়েছে৷ পরে আবার চেষ্টা করুন৷"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"বহুবার চেষ্টা করেছেন। আঙ্গুলের ছাপ নেওয়ার সেন্সর অক্ষম করা হয়েছে।"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনও আঙ্গুলের ছাপ নথিভুক্ত করা হয়নি।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"টিকচিহ্ন দেওয়া নেই"</string>
<string name="selected" msgid="6614607926197755875">"বেছে নেওয়া হয়েছে"</string>
<string name="not_selected" msgid="410652016565864475">"বেছে নেওয়া হয়নি"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max}টির মধ্যে একটি স্টার}one{#টির মধ্যে {max}টি স্টার}other{#টির মধ্যে {max}টি স্টার}}"</string>
<string name="in_progress" msgid="2149208189184319441">"কাজ চলছে"</string>
<string name="whichApplication" msgid="5432266899591255759">"এটি ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"পছন্দের অঞ্চল"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ভাষার নাম লিখুন"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"প্রস্তাবিত"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"সাজেস্ট করা অঞ্চল"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"সাজেস্ট করা ভাষা"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"সাজেস্ট করা অঞ্চল"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"সকল ভাষা"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> অ্যাপকে ডিভাইসের সব লগ অ্যাক্সেসের অনুমতি দিতে চান?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"এককালীন অ্যাক্সেসের অনুমতি দিন"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"অনুমতি দেবেন না"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"ডিভাইস লগে আপনার ডিভাইসে করা অ্যাক্টিভিটি রেকর্ড করা হয়। অ্যাপ সমস্যা খুঁজে তা সমাধান করতে এইসব লগ ব্যবহার করতে পারে।\n\nকিছু লগে সংবেদনশীল তথ্য থাকতে পারে, তাই বিশ্বাস করেন শুধুমাত্র এমন অ্যাপকেই সব ডিভাইসের লগ অ্যাক্সেসের অনুমতি দিন। \n\nআপনি এই অ্যাপকে ডিভাইসের সব লগ অ্যাক্সেস করার অনুমতি না দিলেও, এটি নিজের লগ অ্যাক্সেস করতে পারবে। ডিভাইস প্রস্তুতকারকও আপনার ডিভাইসের কিছু লগ বা তথ্য হয়ত অ্যাক্সেস করতে পারবে।"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"আর দেখতে চাই না"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> অ্যাপটি <xliff:g id="APP_2">%2$s</xliff:g> এর অংশ দেখাতে চায়"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"এডিট করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 8429ebb..f182ab4 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -606,7 +606,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja s otiskom prsta je otkazana."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju s otiskom prsta."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Previše pokušaja. Senzor za otisak prsta je onemogućen."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Pokušajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije prijavljen nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
@@ -1168,8 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
<string name="selected" msgid="6614607926197755875">"odabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Jedna zvjezdica od {max}}one{# zvjezdica od {max}}few{# zvjezdice od {max}}other{# zvjezdica od {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"u toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
@@ -1427,7 +1427,7 @@
<string name="ext_media_browse_action" msgid="344865351947079139">"Istraži"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Prebacite izlaz"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> nedostaje"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"Ponovo ubacite uređaj"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"Ponovo umetnite uređaj"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"Premješta se <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Premještanje podataka"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"Prijenos sadržaja je završen"</string>
@@ -1926,8 +1926,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Izbor regije"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Upišite ime jezika"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Predloženo"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Predloženo"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Predloženi jezici"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Predložene regije"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Svi jezici"</string>
@@ -2052,8 +2051,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Dozvoliti aplikaciji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> da pristupa svim zapisnicima uređaja?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Dozvoli jednokratan pristup"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nemoj dozvoliti"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Zapisnici uređaja bilježe šta se dešava na uređaju. Aplikacije mogu koristiti te zapisnike da pronađu i isprave probleme.\n\nNeki zapisnici mogu sadržavati osjetljive podatke, zato pristup svim zapisnicima uređaja dozvolite samo aplikacijama kojima vjerujete. \n\nAko ne dozvolite ovoj aplikaciji da pristupa svim zapisnicima uređaja, ona i dalje može pristupati svojim zapisnicima. Proizvođač uređaja će možda i dalje biti u stanju pristupiti nekim zapisnicima ili podacima na uređaju."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikazuj ponovo"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi prikazati isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Uredi"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d9a6883..e88bb55 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"S\'ha cancel·lat l\'operació d\'empremta digital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'usuari ha cancel·lat l\'operació d\'empremta digital."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"S\'han produït massa intents. Torna-ho a provar més tard."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"S\'han fet massa intents. S\'ha desactivat el sensor d\'empremtes digitals."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Torna-ho a provar."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No s\'ha registrat cap empremta digital."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes digitals."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionat"</string>
<string name="selected" msgid="6614607926197755875">"seleccionat"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionat"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 estrella de {max}}other{# estrelles de {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en curs"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'acció mitjançant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completa l\'acció amb %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferència de regió"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Escriu el nom de l\'idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggerits"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Recomanades"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Idiomes suggerits"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regions suggerides"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Tots els idiomes"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Vols permetre que <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> accedeixi a tots els registres del dispositiu?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permet l\'accés únic"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"No permetis"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Els registres del dispositiu inclouen informació sobre tot allò que passa al teu dispositiu. Les aplicacions poden utilitzar aquests registres per detectar i corregir problemes.\n\nÉs possible que alguns registres continguin informació sensible; per això només has de donar-hi accés a les aplicacions de confiança. \n\nEncara que no permetis que aquesta aplicació pugui accedir a tots els registres del dispositiu, podrà accedir als seus propis registres. És possible que el fabricant del dispositiu també tingui accés a alguns registres o a informació del teu dispositiu."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"No tornis a mostrar"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vol mostrar porcions de l\'aplicació <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edita"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Consulta les aplicacions actives"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No es pot accedir a la càmera del telèfon des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No es pot accedir a la càmera de la tauleta des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"No s\'hi pot accedir mentre s\'està reproduint en continu. Prova-ho al telèfon."</string>
<string name="system_locale_title" msgid="711882686834677268">"Valor predeterminat del sistema"</string>
<string name="default_card_name" msgid="9198284935962911468">"TARGETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 601af14..d1c4d76 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operace otisku prstu byla zrušena."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Uživatel operaci s otiskem prstu zrušil."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Příliš mnoho pokusů. Zkuste to později."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Příliš mnoho pokusů. Snímač otisků prstů byl deaktivován."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Zkuste to znovu."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nejsou zaregistrovány žádné otisky prstů."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"nevybráno"</string>
<string name="selected" msgid="6614607926197755875">"vybráno"</string>
<string name="not_selected" msgid="410652016565864475">"nevybráno"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Jedna hvězdička z {max}}few{# hvězdičky z {max}}many{# hvězdičky z {max}}other{# hvězdiček z {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"probíhá"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončit akci pomocí aplikace"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončit akci pomocí aplikace %1$s"</string>
@@ -1428,7 +1428,7 @@
<string name="ext_media_browse_action" msgid="344865351947079139">"Otevřít"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Přepnout výstup"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> chybí"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"Znovu vložte zařízení"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"Vložte zařízení znovu"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"Přesouvání aplikace <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Probíhá přesun dat"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"Přenos obsahu je dokončen"</string>
@@ -1567,7 +1567,7 @@
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s – %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s – %3$s"</string>
<string name="storage_internal" msgid="8490227947584914460">"Interní sdílené úložiště"</string>
- <string name="storage_sd_card" msgid="3404740277075331881">"Karta SD"</string>
+ <string name="storage_sd_card" msgid="3404740277075331881">"SD karta"</string>
<string name="storage_sd_card_label" msgid="7526153141147470509">"SD karta <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"Jednotka USB"</string>
<string name="storage_usb_drive_label" msgid="6631740655876540521">"Jednotka USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferovaná oblast"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Zadejte název jazyka"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Navrhované"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Navrženo"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Navrhované jazyky"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Navrhované oblasti"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Všechny jazyky"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Povolit aplikaci <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> přístup ke všem protokolům zařízení?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Povolit jednorázový přístup"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nepovolovat"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Do protokolů zařízení se zaznamenává, co se na zařízení děje. Aplikace tyto protokoly mohou používat k vyhledání a odstranění problémů.\n\nNěkteré protokoly mohou zahrnovat citlivé údaje. Přístup k protokolům zařízení proto povolte pouze aplikacím, kterým důvěřujete. \n\nPokud této aplikaci nepovolíte přístup ke všem protokolům zařízení, bude mít stále přístup ke svým vlastním protokolům. Výrobce zařízení může mít stále přístup k některým protokolům nebo informacím na vašem zařízení."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Příště nezobrazovat"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Aplikace <xliff:g id="APP_0">%1$s</xliff:g> chce zobrazovat ukázky z aplikace <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Upravit"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 06b1336..5191aea 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeraftrykshandlingen blev annulleret."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeraftrykshandlingen blev annulleret af brugeren."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Du har prøvet for mange gange. Prøv igen senere."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Du har brugt for mange forsøg. Fingeraftrykslæseren er deaktiveret."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Prøv igen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"slået fra"</string>
<string name="selected" msgid="6614607926197755875">"valgt"</string>
<string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{En stjerne ud af {max}}one{# stjerne ud af {max}}other{# stjerner ud af {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"i gang"</string>
<string name="whichApplication" msgid="5432266899591255759">"Brug"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Gennemfør handling ved hjælp af %1$s"</string>
@@ -1566,7 +1566,7 @@
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="8490227947584914460">"Intern delt lagerplads"</string>
<string name="storage_sd_card" msgid="3404740277075331881">"SD-kort"</string>
- <string name="storage_sd_card_label" msgid="7526153141147470509">"SD-kort fra <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
+ <string name="storage_sd_card_label" msgid="7526153141147470509">"SD-kort (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"USB-drev"</string>
<string name="storage_usb_drive_label" msgid="6631740655876540521">"USB-drev fra <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB-lager"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Områdeindstilling"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Angiv sprog"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Foreslået"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Forslag"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Foreslåede sprog"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Foreslåede områder"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Alle sprog"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Vil du give <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> adgang til alle enhedslogs?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Tillad engangsadgang"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Tillad ikke"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Enhedslogs registrerer, hvad der sker på din enhed. Apps kan bruge disse logs til at finde og løse problemer.\n\nNogle logs kan indeholde følsomme oplysninger, så giv kun apps, du har tillid til, adgang til alle enhedslogs. \n\nSelvom du ikke giver denne app adgang til alle enhedslogs, kan den stadig tilgå sine egne logs. Producenten af din enhed kan muligvis fortsat tilgå visse logs eller oplysninger på din enhed."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Vis ikke igen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> anmoder om tilladelse til at vise eksempler fra <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Rediger"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Tjek aktive apps"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Kameraet på din telefon kan ikke tilgås via din <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Kameraet på din tablet kan ikke tilgås via din <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Der er ikke adgang til dette indhold under streaming. Prøv på din telefon i stedet."</string>
<string name="system_locale_title" msgid="711882686834677268">"Systemstandard"</string>
<string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c511992..b5af566 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerabdruckvorgang abgebrochen"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Zu viele Versuche. Der Fingerabdrucksensor wurde deaktiviert."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Keine Fingerabdrücke erfasst."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"deaktiviert"</string>
<string name="selected" msgid="6614607926197755875">"ausgewählt"</string>
<string name="not_selected" msgid="410652016565864475">"nicht ausgewählt"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Einer von {max} Sternen}other{# von {max} Sternen}}"</string>
<string name="in_progress" msgid="2149208189184319441">"Noch nicht abgeschlossen"</string>
<string name="whichApplication" msgid="5432266899591255759">"Aktion durchführen mit"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Aktion mit %1$s abschließen"</string>
@@ -1249,10 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps werden gestartet..."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Start wird abgeschlossen..."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du hast die Ein-/Aus-Taste gedrückt — damit wird der Bildschirm ausgeschaltet.\n\nTippe die Taste leicht an, um deinen Fingerabdruck einzurichten."</string>
- <!-- no translation found for fp_power_button_enrollment_title (8997641910928785172) -->
- <skip />
- <!-- no translation found for fp_power_button_enrollment_button_text (8351290204990805109) -->
- <skip />
+ <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tippen, um Display auszuschalten"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Display ausschalten"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Mit der Fingerabdruckprüfung fortfahren?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du hast die Ein-/Aus-Taste gedrückt — damit wird der Bildschirm ausgeschaltet.\n\nTippe die Taste leicht an, um mit deinem Fingerabdruck deine Identität zu bestätigen."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ausschalten"</string>
@@ -1927,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Region auswählen"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Sprache eingeben"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Vorschläge"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Vorschläge"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Vorgeschlagene Sprachen"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Vorgeschlagene Regionen"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Alle Sprachen"</string>
@@ -2053,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> den Zugriff auf alle Geräteprotokolle erlauben?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Einmaligen Zugriff zulassen"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nicht zulassen"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"In Geräteprotokollen wird aufgezeichnet, welche Aktionen auf deinem Gerät ausgeführt werden. Apps können diese Protokolle verwenden, um Probleme zu finden und zu beheben.\n\nEinige Protokolle enthalten unter Umständen vertrauliche Informationen, daher solltest du nur vertrauenswürdigen Apps den Zugriff auf alle Geräteprotokolle erlauben. \n\nWenn du dieser App keinen Zugriff auf alle Geräteprotokolle gewährst, kann sie trotzdem auf ihre eigenen Protokolle zugreifen. Dein Gerätehersteller hat möglicherweise auch Zugriff auf einige Protokolle oder Informationen auf deinem Gerät."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Nicht mehr anzeigen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> möchte Teile von <xliff:g id="APP_2">%2$s</xliff:g> anzeigen"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Bearbeiten"</string>
@@ -2295,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Aktive Apps prüfen"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Zugriff auf die Kamera des Smartphones über dein Gerät (<xliff:g id="DEVICE">%1$s</xliff:g>) nicht möglich"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Zugriff auf die Kamera des Tablets über dein Gerät (<xliff:g id="DEVICE">%1$s</xliff:g>) nicht möglich"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Während des Streamings ist kein Zugriff möglich. Versuch es stattdessen auf deinem Smartphone."</string>
<string name="system_locale_title" msgid="711882686834677268">"Standardeinstellung des Systems"</string>
<string name="default_card_name" msgid="9198284935962911468">"KARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index c73f6db..72290963 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Πάρα πολλές προσπάθειες. Ο αισθητήρας δακτυλικών αποτυπωμάτων απενεργοποιήθηκε."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Δοκιμάστε ξανά."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"μη επιλεγμένο"</string>
<string name="selected" msgid="6614607926197755875">"επιλεγμένο"</string>
<string name="not_selected" msgid="410652016565864475">"μη επιλεγμένο"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Ένα αστέρι από {max}}other{# αστέρια από {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"σε εξέλιξη"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ολοκλήρωση ενέργειας με τη χρήση"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ολοκληρωμένη ενέργεια με χρήση %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Προτίμηση περιοχής"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Εισαγ. όνομα γλώσσας"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Προτεινόμενες"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Προτεινόμενα"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Προτεινόμενες γλώσσες"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Προτεινόμενες περιοχές"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Όλες οι γλώσσες"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Να επιτρέπεται στο <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> η πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής;"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Να επιτρέπεται η πρόσβαση για μία φορά"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Να μην επιτραπεί"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Τα αρχεία καταγραφής συσκευής καταγράφουν ό,τι συμβαίνει στη συσκευή σας. Οι εφαρμογές μπορούν να χρησιμοποιούν αυτά τα αρχεία καταγραφής για να εντοπίζουν και να διορθώνουν ζητήματα.\n\nΟρισμένα αρχεία καταγραφής ενδέχεται να περιέχουν ευαίσθητες πληροφορίες. Ως εκ τούτου, επιτρέψτε την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής μόνο στις εφαρμογές που εμπιστεύεστε. \n\nΕάν δεν επιτρέψετε σε αυτήν την εφαρμογή την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής, η εφαρμογή εξακολουθεί να έχει πρόσβαση στα δικά της αρχεία καταγραφής. Ο κατασκευαστής της συσκευής σας ενδέχεται να εξακολουθεί να έχει πρόσβαση σε ορισμένα αρχεία καταγραφής ή ορισμένες πληροφορίες στη συσκευή σας."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Να μην εμφανισ. ξανά"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Η εφαρμογή <xliff:g id="APP_0">%1$s</xliff:g> θέλει να εμφανίζει τμήματα της εφαρμογής <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Επεξεργασία"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index cd68709..f0980bd 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -605,7 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Too many attempts. Fingerprint sensor disabled."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
@@ -1167,8 +1167,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{One star out of {max}}other{# stars out of {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
@@ -1925,8 +1924,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Region preference"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Type language name"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggested"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Suggested"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Suggested languages"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Suggested regions"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"All languages"</string>
@@ -2051,8 +2049,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index b164a73..5907d88 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -605,7 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Too many attempts. Fingerprint sensor disabled."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
@@ -1167,8 +1167,7 @@
<string name="not_checked" msgid="7972320087569023342">"not checked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{One star out of {max}}other{# stars out of {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
@@ -1925,8 +1924,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Region preference"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Type language name"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggested"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Suggested"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Suggested languages"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Suggested regions"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"All languages"</string>
@@ -2051,8 +2049,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 475073e..ebe3a4e 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -605,7 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Too many attempts. Fingerprint sensor disabled."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
@@ -1167,8 +1167,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{One star out of {max}}other{# stars out of {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
@@ -1925,8 +1924,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Region preference"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Type language name"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggested"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Suggested"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Suggested languages"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Suggested regions"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"All languages"</string>
@@ -2051,8 +2049,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index af3c7f6..5d9c8a2 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -605,7 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Too many attempts. Fingerprint sensor disabled."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
@@ -1167,8 +1167,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{One star out of {max}}other{# stars out of {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
@@ -1925,8 +1924,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Region preference"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Type language name"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggested"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Suggested"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Suggested languages"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Suggested regions"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"All languages"</string>
@@ -2051,8 +2049,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 6ed6dd8..3920362 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -605,7 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation canceled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation canceled by user."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Too many attempts. Fingerprint sensor disabled."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
@@ -1167,8 +1167,7 @@
<string name="not_checked" msgid="7972320087569023342">"not checked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{One star out of {max}}other{# stars out of {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"in progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
@@ -2050,8 +2049,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 269eb18..575476d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Se canceló la operación de huella dactilar."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario canceló la operación de huella dactilar."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Vuelve a intentarlo más tarde."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Realizaste demasiados intentos. Se inhabilitó el sensor de huellas dactilares."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Vuelve a intentarlo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se registraron huellas digitales."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"desactivado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una estrella de {max}}other{# estrellas de {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar la acción mediante"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
@@ -1425,7 +1425,7 @@
<string name="ext_media_unmount_action" msgid="966992232088442745">"Expulsar"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"Explorar"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Cambiar salida"</string>
- <string name="ext_media_missing_title" msgid="3209472091220515046">"No se encuentra dispositivo <xliff:g id="NAME">%s</xliff:g>."</string>
+ <string name="ext_media_missing_title" msgid="3209472091220515046">"Falta <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_missing_message" msgid="4408988706227922909">"Vuelve a insertar dispositivo"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"Transfiriendo la aplicación <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Transfiriendo los datos"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferencia de región"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Nombre del idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugeridos"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Sugerencias"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Idiomas sugeridos"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regiones sugeridas"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Todos los idiomas"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"¿Quieres permitir que <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acceda a todos los registros del dispositivo?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir acceso por única vez"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"No permitir"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Los registros del dispositivo permiten documentar lo que sucede en él. Las apps pueden usar estos registros para encontrar y solucionar problemas.\n\nEs posible que algunos registros del dispositivo contengan información sensible, por lo que solo debes permitir que accedan a todos ellos las apps que sean de tu confianza. \n\nSi no permites que esta app acceda a todos los registros del dispositivo, aún puede acceder a sus propios registros. Además, es posible que el fabricante del dispositivo acceda a algunos registros o información en tu dispositivo."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"No volver a mostrar"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quiere mostrar fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index de87a54..f1b4bdc 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Se ha cancelado la operación de huella digital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario ha cancelado la operación de huella digital."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Vuelve a intentarlo más tarde."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Demasiados intentos. Se ha inhabilitado el sensor de huellas digitales."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Vuelve a intentarlo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se ha registrado ninguna huella digital."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una estrella de {max}}other{# estrellas de {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferencia de región"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Nombre de idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugeridos"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Sugerencias"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Idiomas sugeridos"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regiones sugeridas"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Todos los idiomas"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"¿Permitir que <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acceda a todos los registros del dispositivo?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir el acceso una vez"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"No permitir"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Los registros del dispositivo documentan lo que sucede en tu dispositivo. Las aplicaciones pueden usar estos registros para encontrar y solucionar problemas.\n\nComo algunos registros pueden contener información sensible, es mejor que solo permitas que accedan a ellos las aplicaciones en las que confíes. \n\nAunque no permitas que esta aplicación acceda a todos los registros del dispositivo, aún podrá acceder a sus propios registros. El fabricante de tu dispositivo aún puede acceder a algunos registros o información de tu dispositivo."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"No volver a mostrar"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quiere mostrar fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Consultar aplicaciones activas"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No se puede acceder a la cámara del teléfono desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No se puede acceder a la cámara del tablet desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"No se puede acceder a este contenido durante una emisión. Prueba en tu teléfono."</string>
<string name="system_locale_title" msgid="711882686834677268">"Predeterminado del sistema"</string>
<string name="default_card_name" msgid="9198284935962911468">"TARJETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 54de367..8b85348 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Sõrmejälje toiming tühistati."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kasutaja tühistas sõrmejälje kasutamise."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Liiga palju katseid. Proovige hiljem uuesti."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Liiga palju katseid. Sõrmejäljeandur on keelatud."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Proovige uuesti."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ühtegi sõrmejälge pole registreeritud."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"märkimata"</string>
<string name="selected" msgid="6614607926197755875">"valitud"</string>
<string name="not_selected" msgid="410652016565864475">"pole valitud"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 tärn {max}-st}other{# tärni {max}-st}}"</string>
<string name="in_progress" msgid="2149208189184319441">"pooleli"</string>
<string name="whichApplication" msgid="5432266899591255759">"Lõpetage toiming rakendusega"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Toimingu lõpetamine, kasutades rakendust %1$s"</string>
@@ -1422,8 +1422,8 @@
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"Üksuse <xliff:g id="NAME">%s</xliff:g> väljutamine …"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Ärge eemaldage"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"Seadistus"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"Eemaldamine"</string>
- <string name="ext_media_browse_action" msgid="344865351947079139">"Avastamine"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"Eemalda"</string>
+ <string name="ext_media_browse_action" msgid="344865351947079139">"Avasta"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Vahetage väljundit"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"Üksust <xliff:g id="NAME">%s</xliff:g> pole"</string>
<string name="ext_media_missing_message" msgid="4408988706227922909">"Sisestage seade uuesti"</string>
@@ -1566,9 +1566,9 @@
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="8490227947584914460">"Sisemine jagatud mäluruum"</string>
<string name="storage_sd_card" msgid="3404740277075331881">"SD-kaart"</string>
- <string name="storage_sd_card_label" msgid="7526153141147470509">"Tootja <xliff:g id="MANUFACTURER">%s</xliff:g> SD-kaart"</string>
+ <string name="storage_sd_card_label" msgid="7526153141147470509">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD-kaart"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"USB-ketas"</string>
- <string name="storage_usb_drive_label" msgid="6631740655876540521">"Tootja <xliff:g id="MANUFACTURER">%s</xliff:g> USB-ketas"</string>
+ <string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB-ketas"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB-mäluseade"</string>
<string name="extract_edit_menu_button" msgid="63954536535863040">"Muuda"</string>
<string name="data_usage_warning_title" msgid="9034893717078325845">"Andmekasutuse hoiatus"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Piirkonnaeelistus"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Sisestage keele nimi"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Soovitatud"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Soovitatud"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Soovitatud keeled"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Soovitatud piirkonnad"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Kõik keeled"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Kas anda rakendusele <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> juurdepääs kõigile seadmelogidele?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Luba ühekordne juurdepääs"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ära luba"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Seadmelogid jäädvustavad, mis teie seadmes toimub. Rakendused saavad neid logisid kasutada probleemide tuvastamiseks ja lahendamiseks.\n\nMõned logid võivad sisaldada tundlikku teavet, seega lubage juurdepääs kõigile seadmelogidele ainult rakendustele, mida usaldate. \n\nKui te ei luba sellel rakendusel kõigile seadmelogidele juurde pääseda, pääseb see siiski juurde oma logidele. Teie seadme tootja võib teie seadmes siiski teatud logidele või teabele juurde pääseda."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ära kuva uuesti"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Rakendus <xliff:g id="APP_0">%1$s</xliff:g> soovib näidata rakenduse <xliff:g id="APP_2">%2$s</xliff:g> lõike"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Muuda"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Vaadake aktiivseid rakendusi"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Teie seadmest <xliff:g id="DEVICE">%1$s</xliff:g> ei pääse telefoni kaamerale juurde"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Teie seadmest <xliff:g id="DEVICE">%1$s</xliff:g> ei pääse tahvelarvuti kaamerale juurde"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Sellele ei pääse voogesituse ajal juurde. Proovige juurde pääseda oma telefonis."</string>
<string name="system_locale_title" msgid="711882686834677268">"Süsteemi vaikeseade"</string>
<string name="default_card_name" msgid="9198284935962911468">"KAART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 25c6678..45cfaf1 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -52,7 +52,7 @@
<string name="imei" msgid="2157082351232630390">"IMEIa"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"Sarrerako deien identifikazio-zerbitzua"</string>
- <string name="ClirMmi" msgid="6752346475055446417">"Ezkutatu irteerako deitzailearen IDa"</string>
+ <string name="ClirMmi" msgid="6752346475055446417">"Ezkutatu irteerako deitzailearen identitatea"</string>
<string name="ColpMmi" msgid="4736462893284419302">"Konektatutako linearen IDa"</string>
<string name="ColrMmi" msgid="5889782479745764278">"Konektatutako linearen ID murriztapena"</string>
<string name="CfMmi" msgid="8390012691099787178">"Dei-desbideratzea"</string>
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Hatz-markaren eragiketa bertan behera utzi da."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Erabiltzaileak bertan behera utzi du hatz-marka bidezko eragiketa."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Saiakera gehiegi egin dituzu. Saiatu berriro geroago."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Saiakera gehiegi egin dituzu. Desgaitu egin da hatz-marken sentsorea."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Saiatu berriro."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ez da erregistratu hatz-markarik."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"markatu gabe"</string>
<string name="selected" msgid="6614607926197755875">"hautatuta"</string>
<string name="not_selected" msgid="410652016565864475">"hautatu gabe"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} izarretatik bat}other{{max} izarretatik #}}"</string>
<string name="in_progress" msgid="2149208189184319441">"abian"</string>
<string name="whichApplication" msgid="5432266899591255759">"Gauzatu ekintza hau erabilita:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Osatu ekintza %1$s erabiliz"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Lurralde-hobespena"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Adierazi hizkuntza"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Iradokitakoak"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Iradokitakoak"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Iradokitako hizkuntzak"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Iradokitako lurraldeak"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Hizkuntza guztiak"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Gailuko erregistro guztiak atzitzeko baimena eman nahi diozu <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> aplikazioari?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Eman behin erabiltzeko baimena"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ez eman baimenik"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak atzitzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak atzitzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ez erakutsi berriro"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> aplikazioak <xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakutsi nahi ditu"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index f59c2e1..df5bca5 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"عملکرد اثر انگشت لغو شد."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"کاربر عملیات اثر انگشت را لغو کرد"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"تلاشهای زیادی انجام شده است. بعداً دوباره امتحان کنید."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"تلاشهای بسیاری زیادی انجام شده است. حسگر اثر انگشت غیرفعال شد."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"دوباره امتحان کنید."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"اثر انگشتی ثبت نشده است."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
<string name="selected" msgid="6614607926197755875">"انتخاب شده"</string>
<string name="not_selected" msgid="410652016565864475">"انتخاب نشده"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{یک ستاره از {max} ستاره}one{# ستاره از {max} ستاره}other{# ستاره از {max} ستاره}}"</string>
<string name="in_progress" msgid="2149208189184319441">"درحال انجام"</string>
<string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"تکمیل کنش بااستفاده از %1$s"</string>
@@ -1422,7 +1422,7 @@
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"درحال بیرون راندن <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"جدا نکنید"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"راهاندازی"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"بیرون راندن"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"خارج کردن"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"کاوش"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"تغییر خروجی"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> وجود ندارد"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"اولویتهای منطقه"</string>
<string name="search_language_hint" msgid="7004225294308793583">"نام زبان را تایپ کنید"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"پیشنهادی"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"پیشنهادی"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"زبانهای پیشنهادی"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"مناطق پیشنهادی"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"همه زبانها"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"به <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> اجازه میدهید به همه گزارشهای دستگاه دسترسی داشته باشد؟"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"مجاز کردن دسترسی یکباره"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"اجازه ندادن"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"گزارشهای دستگاه آنچه را در دستگاهتان رخ میدهد ثبت میکند. برنامهها میتوانند از این گزارشها برای پیدا کردن مشکلات و رفع آنها استفاده کنند.\n\nبرخیاز گزارشها ممکن است حاوی اطلاعات حساس باشند، بنابراین فقط به برنامههای مورداعتمادتان اجازه دسترسی به همه گزارشهای دستگاه را بدهید. \n\nاگر به این برنامه اجازه ندهید به همه گزارشهای دستگاه دسترسی داشته باشد، همچنان میتواند به گزارشهای خودش دسترسی داشته باشد. سازنده دستگاه نیز ممکن است همچنان بتواند به برخیاز گزارشها یا اطلاعات دستگاهتان دسترسی داشته باشد."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"دوباره نشان داده نشود"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> میخواهد تکههای <xliff:g id="APP_2">%2$s</xliff:g> را نشان دهد"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ویرایش"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 4f70e82..375d7af 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Sormenjälkitoiminto peruutettiin."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Käyttäjä peruutti sormenjälkitoiminnon."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Liian monta yritystä. Sormenjälkitunnistin poistettu käytöstä."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Yritä uudelleen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Sormenjälkiä ei ole otettu käyttöön."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ei valittu"</string>
<string name="selected" msgid="6614607926197755875">"valittu"</string>
<string name="not_selected" msgid="410652016565864475">"ei valittu"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1/{max} tähteä}other{#/{max} tähteä}}"</string>
<string name="in_progress" msgid="2149208189184319441">"käynnissä"</string>
<string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen sovellusta"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Suorita sovelluksella %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Alueasetus"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Anna kielen nimi"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Ehdotukset"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Ehdotettu"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Kieliehdotukset"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Alue-ehdotukset"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Kaikki kielet"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Saako <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> pääsyn kaikkiin laitelokeihin?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Salli kertaluonteinen pääsy"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Älä salli"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Laitteen tapahtumat tallentuvat laitelokeihin. Niiden avulla sovellukset voivat löytää ja korjata ongelmia.\n\nJotkin lokit voivat sisältää arkaluontoista tietoa, joten salli pääsy kaikkiin laitelokeihin vain sovelluksille, joihin luotat. \n\nJos et salli tälle sovellukselle pääsyä kaikkiin laitelokeihin, sillä on kuitenkin pääsy sen omiin lokeihin. Laitteen valmistajalla voi olla pääsy joihinkin lokeihin tai tietoihin laitteella."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Älä näytä uudelleen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> haluaa näyttää osia sovelluksesta <xliff:g id="APP_2">%2$s</xliff:g>."</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Muokkaa"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Tarkista aktiiviset sovellukset"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> ei pääse puhelimen kameraan"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> ei pääse tabletin kameraan"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Sisältöön ei saa pääsyä striimauksen aikana. Kokeile striimausta puhelimella."</string>
<string name="system_locale_title" msgid="711882686834677268">"Järjestelmän oletusarvo"</string>
<string name="default_card_name" msgid="9198284935962911468">"Kortti: <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 6e9250f..baf6f695 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale numérique annulée."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'opération d\'empreinte digitale a été annulée par l\'utilisateur."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Trop de tentatives. Veuillez réessayer plus tard."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Trop de tentatives. Capteur d\'empreintes digitales désactivé."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Réessayer."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"non coché"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Une étoile sur {max}}one{# étoile sur {max}}other{# étoiles sur {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Continuer avec %1$s"</string>
@@ -1423,7 +1423,7 @@
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Ne pas retirer"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"Configurer"</string>
<string name="ext_media_unmount_action" msgid="966992232088442745">"Éjecter"</string>
- <string name="ext_media_browse_action" msgid="344865351947079139">"Découvrir"</string>
+ <string name="ext_media_browse_action" msgid="344865351947079139">"Explorer"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Changer de sortie"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"Mémoire de stockage <xliff:g id="NAME">%s</xliff:g> manquante"</string>
<string name="ext_media_missing_message" msgid="4408988706227922909">"Insérez l\'appareil de nouveau"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Préférences régionales"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Entrez la langue"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggestions"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Suggestions"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Langues suggérées"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Régions suggérées"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Toutes les langues"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Autoriser <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> à accéder à l\'ensemble des journaux de l\'appareil?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Autoriser un accès unique"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne pas autoriser"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Les journaux de l\'appareil enregistrent ce qui se passe sur celui-ci. Les applications peuvent utiliser ces journaux pour trouver et résoudre des problèmes.\n\nCertains journaux peuvent contenir des renseignements confidentiels. N\'autorisez donc que les applications auxquelles vous faites confiance puisque celles-ci pourront accéder à l\'ensemble des journaux de l\'appareil. \n\nMême si vous n\'autorisez pas cette application à accéder à l\'ensemble des journaux de l\'appareil, elle aura toujours accès à ses propres journaux. Le fabricant de votre appareil pourrait toujours être en mesure d\'accéder à certains journaux ou renseignements sur votre appareil."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne plus afficher"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> souhaite afficher <xliff:g id="APP_2">%2$s</xliff:g> tranches"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Modifier"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index da3c802..95d06f4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale annulée."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Trop de tentatives. Veuillez réessayer plus tard."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Trop de tentatives. Lecteur d\'empreinte digitale désactivé."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Veuillez réessayer."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"désactivé"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 étoile sur {max}}one{# étoile sur {max}}other{# étoiles sur {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Terminer l\'action avec %1$s"</string>
@@ -1425,7 +1425,7 @@
<string name="ext_media_unmount_action" msgid="966992232088442745">"Éjecter"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"Parcourir"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Changer de sortie"</string>
- <string name="ext_media_missing_title" msgid="3209472091220515046">"Mémoire de stockage \"<xliff:g id="NAME">%s</xliff:g>\" manquante"</string>
+ <string name="ext_media_missing_title" msgid="3209472091220515046">"Support \"<xliff:g id="NAME">%s</xliff:g>\" manquant"</string>
<string name="ext_media_missing_message" msgid="4408988706227922909">"Insérez le périphérique"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"Transfert de l\'application <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Déplacement des données en cours"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Préférences régionales"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Saisissez la langue"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggestions"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Suggestions"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Langues suggérées"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Régions suggérées"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Toutes les langues"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Autoriser <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> à accéder à tous les journaux de l\'appareil ?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Autoriser un accès unique"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne pas autoriser"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Les journaux enregistrent ce qui se passe sur votre appareil. Les applis peuvent les utiliser pour rechercher et résoudre les problèmes.\n\nCertains journaux pouvant contenir des infos sensibles, autorisez uniquement les applis de confiance à accéder à tous les journaux de l\'appareil. \n\nSi vous refusez à cette appli l\'accès à tous les journaux de l\'appareil, elle a quand même accès aux siens. Le fabricant de l\'appareil peut accéder à certains journaux ou certaines infos sur votre appareil."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne plus afficher"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> souhaite afficher des éléments de <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Modifier"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Vérifier les applis actives"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Impossible d\'accéder à l\'appareil photo du téléphone depuis votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Impossible d\'accéder à l\'appareil photo de la tablette depuis votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Impossible d\'accéder à cela pendant le streaming. Essayez plutôt sur votre téléphone."</string>
<string name="system_locale_title" msgid="711882686834677268">"Paramètre système par défaut"</string>
<string name="default_card_name" msgid="9198284935962911468">"CARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 0109665..2dd092d 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Cancelouse a operación da impresión dixital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"O usuario cancelou a operación da impresión dixital."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Téntao de novo máis tarde."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Demasiados intentos. Desactivouse o sensor de impresión dixital."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Téntao de novo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Non se rexistraron impresións dixitais."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"non seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"elemento seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"elemento non seleccionado"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 estrela de {max}}other{# estrelas de {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar a acción usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar a acción usando %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferencia de rexión"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Escribe o nome do idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suxeridos"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Rexións suxeridas"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Idiomas suxeridos"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Rexións suxeridas"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Todos os idiomas"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Queres permitir que a aplicación <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acceda a todos os rexistros do dispositivo?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir acceso unha soa vez"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Non permitir"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Os rexistros do dispositivo dan conta do que ocorre neste. As aplicacións poden usalos para buscar problemas e solucionalos.\n\nAlgúns poden conter información confidencial, polo que che recomendamos que só permitas que accedan a todos os rexistros do dispositivo as aplicacións nas que confíes. \n\nEsta aplicación pode acceder aos seus propios rexistros aínda que non lle permitas acceder a todos. É posible que o fabricante do dispositivo teña acceso a algúns rexistros ou á información do teu dispositivo."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Non amosar outra vez"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quere mostrar fragmentos de aplicación de <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Comprobar aplicacións activas"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Non se puido acceder á cámara do teléfono desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Non se puido acceder á cámara da tableta desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Non se puido acceder a este contido durante a reprodución en tempo real. Téntao desde o teléfono."</string>
<string name="system_locale_title" msgid="711882686834677268">"Opción predeterminada do sistema"</string>
<string name="default_card_name" msgid="9198284935962911468">"TARXETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 3bb7c38..d170302 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ફિંગરપ્રિન્ટ ઓપરેશન રદ કર્યું."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા વપરાશકર્તાએ રદ કરી."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ઘણા બધા પ્રયત્નો. પછીથી ફરી પ્રયાસ કરો."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"ઘણા વધુ પ્રયત્નો. ફિંગરપ્રિન્ટ સેન્સર અક્ષમ કરવામાં આવ્યું છે."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ચેક કર્યું નથી"</string>
<string name="selected" msgid="6614607926197755875">"પસંદ કરેલી"</string>
<string name="not_selected" msgid="410652016565864475">"પસંદ નહીં કરેલી"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max}માંથી એક સ્ટાર}one{{max}માંથી # સ્ટાર}other{{max}માંથી # સ્ટાર}}"</string>
<string name="in_progress" msgid="2149208189184319441">"પ્રક્રિયા ચાલુ છે"</string>
<string name="whichApplication" msgid="5432266899591255759">"આના ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
@@ -1426,7 +1426,7 @@
<string name="ext_media_browse_action" msgid="344865351947079139">"અન્વેષણ કરો"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"આઉટપુટ સ્વિચ કરો"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> ખૂટે છે"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"ફરીથી ઉપકરણ દાખલ કરો"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"ફરીથી ડિવાઇસ દાખલ કરો"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"<xliff:g id="NAME">%s</xliff:g> ખસેડી રહ્યાં છીએ"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"ડેટાને ખસેડી રહ્યાં છીએ"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"કન્ટેન્ટ ટ્રાન્સફર કરવાનું પૂર્ણ થયું"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"પ્રદેશ પસંદગી"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ભાષાનું નામ ટાઇપ કરો"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"સૂચવેલી ભાષા"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"સૂચવેલા"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"સૂચવેલી ભાષાઓ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"સૂચવેલા પ્રદેશો"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"બધી ભાષાઓ"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>ને ડિવાઇસનો બધો લૉગ ઍક્સેસ કરવાની મંજૂરી આપવી છે?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"એક-વખતના ઍક્સેસની મંજૂરી આપો"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"મંજૂરી આપશો નહીં"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"તમારા ડિવાઇસ પર થતી કામગીરીને ડિવાઇસ લૉગ રેકોર્ડ કરે છે. ઍપ આ લૉગનો ઉપયોગ સમસ્યાઓ શોધી તેનું નિરાકરણ કરવા માટે કરી શકે છે.\n\nઅમુક લૉગમાં સંવેદનશીલ માહિતી હોઈ શકે, આથી ડિવાઇસનો બધો લૉગ ઍક્સેસ કરવાની મંજૂરી માત્ર તમારી વિશ્વાસપાત્ર ઍપને જ આપો. \n\nજો તમે આ ઍપને ડિવાઇસનો બધો લૉગ ઍક્સેસ કરવાની મંજૂરી ન આપો, તો પણ તે તેના પોતાના લૉગ ઍક્સેસ કરી શકે છે. તમારા ડિવાઇસના નિર્માતા હજુ પણ કદાચ તમારા ડિવાઇસ પર અમુક લૉગ અથવા માહિતી ઍક્સેસ કરી શકે છે."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"ફરીથી બતાવશો નહીં"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>એ <xliff:g id="APP_2">%2$s</xliff:g> સ્લાઇસ બતાવવા માગે છે"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ફેરફાર કરો"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"સક્રિય ઍપ ચેક કરો"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પરથી ફોનના કૅમેરાનો ઍક્સેસ કરી શકતાં નથી"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પરથી ટૅબ્લેટના કૅમેરાનો ઍક્સેસ કરી શકતાં નથી"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"સ્ટ્રીમ કરતી વખતે આ ઍક્સેસ કરી શકાતું નથી. તેના બદલે તમારા ફોન પર પ્રયાસ કરો."</string>
<string name="system_locale_title" msgid="711882686834677268">"સિસ્ટમ ડિફૉલ્ટ"</string>
<string name="default_card_name" msgid="9198284935962911468">"કાર્ડ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b8a0fc2..d970498 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फ़िंगरप्रिंट ऑपरेशन रोक दिया गया."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"बहुत ज़्यादा प्रयास कर लिए गए हैं. बाद में फिर से प्रयास करें."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"बहुत ज़्यादा कोशिशें. फ़िंगरप्रिंट सेंसर अक्षम है."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"फिर से कोशिश करें."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"बंद है"</string>
<string name="selected" msgid="6614607926197755875">"चुना गया"</string>
<string name="not_selected" msgid="410652016565864475">"नहीं चुना गया"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} में से एक स्टार}one{{max} में से # स्टार}other{{max} में से # स्टार}}"</string>
<string name="in_progress" msgid="2149208189184319441">"जारी है"</string>
<string name="whichApplication" msgid="5432266899591255759">"इसका इस्तेमाल करके कार्रवाई को पूरा करें"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s का उपयोग करके कार्रवाई पूरी करें"</string>
@@ -1425,7 +1425,7 @@
<string name="ext_media_unmount_action" msgid="966992232088442745">"निकालें"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"एक्सप्लोर करें"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"आउटपुट बदलें"</string>
- <string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> गुम है"</string>
+ <string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> नहीं मिल रहा है"</string>
<string name="ext_media_missing_message" msgid="4408988706227922909">"डिवाइस को दोबारा लगाएं"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"<xliff:g id="NAME">%s</xliff:g> को ले जाया जा रहा है"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"डेटा ले जाया जा रहा है"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"क्षेत्र प्राथमिकता"</string>
<string name="search_language_hint" msgid="7004225294308793583">"भाषा का नाम लिखें"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"दिए गए सुझाव"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"सुझाए गए देश/इलाके"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"सुझाई गई भाषाएं"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"सुझाए गए इलाके"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"सभी भाषाएं"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"क्या <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> को डिवाइस लॉग का ऐक्सेस देना है?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"एक बार ऐक्सेस करने की अनुमति दें"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"अनुमति न दें"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"डिवाइस लॉग में, आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें ठीक करने के लिए करते हैं.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस लॉग का ऐक्सेस दें. \n\nअगर इस ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी यह डिवाइस पर अपने लॉग को ऐक्सेस कर सकता है. डिवाइस को बनाने वाली कंपनी अब भी डिवाइस के कुछ लॉग या जानकारी को ऐक्सेस कर सकती है."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"फिर से न दिखाएं"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाना चाहता है"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"बदलाव करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index a638c7e..1a0b1c6 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -606,7 +606,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja otiska prsta otkazana je."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Radnju s otiskom prsta otkazao je korisnik."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Senzor otiska prsta onemogućen je zbog previše pokušaja."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Pokušajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registriran nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string>
@@ -1168,8 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije potvrđeno"</string>
<string name="selected" msgid="6614607926197755875">"odabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Jedna zvjezdica od {max}}one{# zvjezdica od {max}}few{# zvjezdice od {max}}other{# zvjezdica od {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"u tijeku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Radnju dovrši pomoću stavke"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršavanje radnje pomoću aplikacije %1$s"</string>
@@ -1926,8 +1926,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Postavke regije"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Unesite naziv jezika"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Predloženo"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Predloženo"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Predloženi jezici"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Predložene regije"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Svi jezici"</string>
@@ -2052,8 +2051,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Želite li dopustiti aplikaciji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> da pristupa svim zapisnicima uređaja?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Omogući jednokratni pristup"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nemoj dopustiti"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"U zapisnicima uređaja bilježi se što se događa na uređaju. Aplikacije mogu koristiti te zapisnike kako bi pronašle i riješile poteškoće.\n\nNeki zapisnici mogu sadržavati osjetljive podatke, pa pristup svim zapisnicima uređaja odobrite samo pouzdanim aplikacijama. \n\nAko ne dopustite ovoj aplikaciji da pristupa svim zapisnicima uređaja, ona i dalje može pristupati svojim zapisnicima. Proizvođač vašeg uređaja i dalje može pristupati nekim zapisnicima ili podacima na vašem uređaju."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikazuj ponovo"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> želi prikazivati isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Uredi"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index e71a92f..24dc9b6 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Ujjlenyomattal kapcsolatos művelet megszakítva"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Az ujjlenyomattal kapcsolatos műveletet a felhasználó megszakította."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Túl sok próbálkozás. Próbálja újra később."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Túl sok próbálkozás. Ujjlenyomat-érzékelő letiltva."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Próbálkozzon újra."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nincsenek regisztrált ujjlenyomatok."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"nincs kiválasztva"</string>
<string name="selected" msgid="6614607926197755875">"kiválasztva"</string>
<string name="not_selected" msgid="410652016565864475">"nincs kiválasztva"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} csillagból egy}other{{max} csillagból #}}"</string>
<string name="in_progress" msgid="2149208189184319441">"folyamatban"</string>
<string name="whichApplication" msgid="5432266899591255759">"Művelet végrehajtása a következővel:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Művelet elvégzése a(z) %1$s segítségével"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Régió beállítása"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Adja meg a nyelvet"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Javasolt"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Javasolt"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Javasolt nyelvek"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Javasolt régiók"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Minden nyelv"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Engedélyezi a(z) <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> számára, hogy hozzáférjen az összes eszköznaplóhoz?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Egyszeri hozzáférés engedélyezése"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Tiltás"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Az eszköznaplók rögzítik, hogy mi történik az eszközén. Az alkalmazások ezeket a naplókat használhatják a problémák megkeresésére és kijavítására.\n\nBizonyos naplók bizalmas adatokat is tartalmazhatnak, ezért csak olyan alkalmazások számára engedélyezze az összes eszköznaplóhoz való hozzáférést, amelyekben megbízik. \n\nHa nem engedélyezi ennek az alkalmazásnak, hogy hozzáférjen az összes eszköznaplójához, az app továbbra is hozzáférhet a saját naplóihoz. Előfordulhat, hogy az eszköz gyártója továbbra is hozzáfér az eszközön található bizonyos naplókhoz és adatokhoz."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne jelenjen meg újra"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"A(z) <xliff:g id="APP_0">%1$s</xliff:g> alkalmazás részleteket szeretne megjeleníteni a(z) <xliff:g id="APP_2">%2$s</xliff:g> alkalmazásból"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Szerkesztés"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 9918859..b9206c3 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Իսկորոշումը մատնահետքի միջոցով չեղարկվեց:"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Մատնահետքով նույնականացման գործողությունը չեղարկվել է օգտատիրոջ կողմից:"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Չափից շատ փորձ եք կատարել: Փորձեք նորից քիչ հետո:"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Չափից շատ փորձ եք կատարել: Մատնահետքերի սկաներն անջատվել է:"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Փորձեք նորից:"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Գրանցված մատնահետք չկա:"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"նշված չէ"</string>
<string name="selected" msgid="6614607926197755875">"ընտրված է"</string>
<string name="not_selected" msgid="410652016565864475">"ընտրված չէ"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Մեկ աստղ՝ {max}-ից}one{# աստղ՝ {max}-ից}other{# աստղ՝ {max}-ից}}"</string>
<string name="in_progress" msgid="2149208189184319441">"ընթացքում է"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ավարտել գործողությունը` օգտագործելով"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Եզրափակել գործողությունը՝ օգտագործելով %1$s"</string>
@@ -1422,7 +1422,7 @@
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"<xliff:g id="NAME">%s</xliff:g> հիշասարքն անջատվում է"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Չհեռացնեք սարքը"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"Կարգավորել"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"Անջատել"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"Հանել"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"Ուսումնասիրել"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Աուդիոելքի սարքի փոխարկում"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g>-ը տեղադրված չէ"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Նախընտրելի տարածաշրջան"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Մուտքագրեք լեզուն"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Առաջարկվող"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Առաջարկվող"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Առաջարկվող լեզուներ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Առաջարկվող տարածաշրջաններ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Բոլոր լեզուները"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Հասանելի դարձնե՞լ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> հավելվածին սարքի բոլոր մատյանները"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Թույլատրել մեկանգամյա մուտքը"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Չթույլատրել"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Այն, ինչ տեղի է ունենում ձեր սարքում, գրանցվում է սարքի մատյաններում։ Հավելվածները կարող են դրանք օգտագործել անսարքությունները հայտնաբերելու և վերացնելու նպատակով։\n\nՔանի որ որոշ մատյաններ անձնական տեղեկություններ են պարունակում, խորհուրդ ենք տալիս հասանելի դարձնել ձեր սարքի բոլոր մատյանները միայն այն հավելվածներին, որոնց վստահում եք։ \n\nԵթե այս հավելվածին նման թույլտվություն չեք տվել, դրան նախկինի պես հասանելի կլինեն իր մատյանները։ Հնարավոր է՝ ձեր սարքի արտադրողին ևս հասանելի լինեն սարքի որոշ մատյաններ և տեղեկություններ։"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Այլևս ցույց չտալ"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> հավելվածն ուզում է ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Փոփոխել"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Ստուգել ակտիվ հավելվածները"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Հնարավոր չէ օգտագործել հեռախոսի տեսախցիկը ձեր <xliff:g id="DEVICE">%1$s</xliff:g> սարքից"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Հնարավոր չէ օգտագործել պլանշետի տեսախցիկը ձեր <xliff:g id="DEVICE">%1$s</xliff:g> սարքից"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Այս բովանդակությունը հասանելի չէ հեռարձակման ընթացքում։ Օգտագործեք ձեր հեռախոսը։"</string>
<string name="system_locale_title" msgid="711882686834677268">"Կանխադրված"</string>
<string name="default_card_name" msgid="9198284935962911468">"ՔԱՐՏ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 0914da5..30f98fd 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operasi sidik jari dibatalkan."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operasi sidik jari dibatalkan oleh pengguna."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Terlalu banyak upaya. Coba lagi nanti."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Terlalu sering dicoba. Sensor sidik jari dinonaktifkan."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Coba lagi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tidak ada sidik jari yang terdaftar."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
<string name="selected" msgid="6614607926197755875">"dipilih"</string>
<string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Satu bintang dari {max}}other{# bintang dari {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferensi wilayah"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Ketik nama bahasa"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Disarankan"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Disarankan"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Bahasa yang disarankan"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Wilayah yang disarankan"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Semua bahasa"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Izinkan <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> mengakses semua log perangkat?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Izinkan akses satu kali"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Jangan izinkan"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Log perangkat merekam hal-hal yang terjadi di perangkat Anda. Aplikasi dapat menggunakan log ini untuk menemukan dan memperbaiki masalah.\n\nBeberapa log mungkin berisi info sensitif, jadi hanya izinkan aplikasi yang Anda percayai untuk mengakses semua log perangkat. \n\nJika Anda tidak mengizinkan aplikasi ini mengakses semua log perangkat, aplikasi masih dapat mengakses log-nya sendiri. Produsen perangkat masih dapat mengakses beberapa log atau info di perangkat Anda."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Jangan tampilkan lagi"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ingin menampilkan potongan <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0e86fc1..20bf9bf 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Hætt við fingrafarsaðgerð."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Notandi hætti við að nota fingrafar."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Of margar tilraunir. Reyndu aftur síðar."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Of margar tilraunir. Fingrafaralesari gerður óvirkur."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Reyndu aftur."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Engin fingraför hafa verið skráð."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ekki valið"</string>
<string name="selected" msgid="6614607926197755875">"valið"</string>
<string name="not_selected" msgid="410652016565864475">"ekki valið"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Ein stjarna af {max}}one{# stjarna af {max}}other{# stjörnur af {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"í gangi"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ljúka aðgerð með"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ljúka aðgerð með %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Svæðisval"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Sláðu inn heiti tungumáls"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Tillögur"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Tillögur"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Tillögur að tungumálum"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Tillögur að svæðum"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Öll tungumál"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Veita <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> aðgang að öllum annálum í tækinu?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Leyfa aðgang í eitt skipti"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ekki leyfa"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Annálar tækisins skrá það sem gerist í tækinu. Forrit geta notað þessa annála til að finna og lagfæra vandamál.\n\nTilteknir annálar innihalda viðkvæmar upplýsingar og því skaltu einungis veita forritum sem þú treystir aðgang að öllum annálum tækisins. \n\nEf þú veitir þessu forriti ekki aðgang að öllum annálum tækisins hefur það áfram aðgang að eigin annálum. Framleiðandi tækisins getur þó hugsanlega opnað tiltekna annála eða upplýsingar í tækinu."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ekki sýna aftur"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vill sýna sneiðar úr <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Breyta"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Skoða virk forrit"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ekki er hægt að opna myndavél símans úr <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ekki er hægt að opna myndavél spjaldtölvunnar úr <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Ekki er hægt að opna þetta á meðan streymi stendur yfir. Prófaðu það í símanum í staðinn."</string>
<string name="system_locale_title" msgid="711882686834677268">"Sjálfgildi kerfis"</string>
<string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 0ade2dda..330100c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operazione associata all\'impronta annullata."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operazione di autenticazione dell\'impronta annullata dall\'utente."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Troppi tentativi. Riprova più tardi."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Troppi tentativi. Sensore di impronte disattivato."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Riprova."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nessuna impronta digitale registrata."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"deselezionato"</string>
<string name="selected" msgid="6614607926197755875">"selezionato"</string>
<string name="not_selected" msgid="410652016565864475">"non selezionato"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una stella su {max}}other{# stelle su {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"In corso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'azione con"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completamento azione con %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Regione preferita"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Digita nome lingua"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggerite"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Suggerite"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Lingue suggerite"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regioni consigliate"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Tutte le lingue"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Consentire all\'app <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> di accedere a tutti i log del dispositivo?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Consenti accesso una tantum"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Non consentire"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"I log del dispositivo registrano tutto ciò che succede sul tuo dispositivo. Le app possono usare questi log per individuare problemi e correggerli.\n\nAlcuni log potrebbero contenere informazioni sensibili, quindi concedi l\'accesso a tutti i log del dispositivo soltanto alle app attendibili. \n\nSe le neghi l\'accesso a tutti i log del dispositivo, questa app può comunque accedere ai propri log. Il produttore del tuo dispositivo potrebbe essere comunque in grado di accedere ad alcuni log o informazioni sul dispositivo."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Non mostrare più"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"L\'app <xliff:g id="APP_0">%1$s</xliff:g> vuole mostrare porzioni dell\'app <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Modifica"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verifica le app attive"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Impossibile accedere alla fotocamera del telefono dal tuo <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Impossibile accedere alla fotocamera del tablet dal tuo <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Impossibile accedere a questi contenuti durante lo streaming. Prova a usare il telefono."</string>
<string name="system_locale_title" msgid="711882686834677268">"Predefinita di sistema"</string>
<string name="default_card_name" msgid="9198284935962911468">"SCHEDA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 7db42df..40a3a7d 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"פעולת טביעת האצבע בוטלה."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"פעולת טביעת האצבע בוטלה על ידי המשתמש."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"יותר מדי ניסיונות. חיישן טביעות האצבע הושבת."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"כדאי לנסות שוב."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נסרקו טביעות אצבע."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר הזה אין חיישן טביעות אצבע."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"לא מסומן"</string>
<string name="selected" msgid="6614607926197755875">"נבחר"</string>
<string name="not_selected" msgid="410652016565864475">"לא נבחר"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{כוכב אחד מתוך {max}}two{# כוכבים מתוך {max}}many{# כוכבים מתוך {max}}other{# כוכבים מתוך {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"בתהליך"</string>
<string name="whichApplication" msgid="5432266899591255759">"השלמת הפעולה באמצעות"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"השלמת הפעולה באמצעות %1$s"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"העדפת אזור"</string>
<string name="search_language_hint" msgid="7004225294308793583">"יש להקליד את שם השפה"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"הצעות"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"הצעות"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"השפות המוצעות"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"האזורים המוצעים"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"כל השפות"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"לתת לאפליקציה <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> הרשאת גישה לכל יומני המכשיר?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"הרשאת גישה חד-פעמית"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"אין אישור"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"ביומני המכשיר מתועדת הפעילות במכשיר. האפליקציות יכולות להשתמש ביומנים האלה כדי למצוא בעיות ולפתור אותן.\n\nהמידע בחלק מהיומנים יכול להיות רגיש, לכן יש לתת הרשאת גישה לכל יומני המכשיר רק לאפליקציות מהימנות. \n\nגם אם האפליקציה הזו לא תקבל הרשאת גישה לכל יומני המכשיר, היא תוכל לגשת ליומנים שלה. יכול להיות שליצרן המכשיר עדיין תהיה גישה לחלק מהיומנים או למידע במכשיר שלך."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"אין להציג שוב"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> רוצה להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"עריכה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 7ac5166..b1373c6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋の操作をキャンセルしました。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"指紋の操作がユーザーによりキャンセルされました。"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"所定の回数以上間違えました。しばらくしてからもう一度お試しください。"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"試行回数が上限を超えました。指紋認証センサーを無効にしました。"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"もう一度お試しください。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"指紋が登録されていません。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"OFF"</string>
<string name="selected" msgid="6614607926197755875">"選択済み"</string>
<string name="not_selected" msgid="410652016565864475">"未選択"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} つ星中 1 つ星の評価}other{{max} つ星中 # つ星の評価}}"</string>
<string name="in_progress" msgid="2149208189184319441">"進行中"</string>
<string name="whichApplication" msgid="5432266899591255759">"アプリケーションを選択"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sを使用してアクションを完了"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"地域設定"</string>
<string name="search_language_hint" msgid="7004225294308793583">"言語名を入力"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"言語の候補"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"候補"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"言語の候補"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"地域の候補"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"すべての言語"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> にすべてのデバイスログへのアクセスを許可しますか?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"1 回限りのアクセスを許可"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"許可しない"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"デバイスのログに、このデバイスで発生したことが記録されます。アプリは問題を検出、修正するためにこれらのログを使用することができます。\n\nログによっては機密性の高い情報が含まれている可能性があるため、すべてのデバイスログへのアクセスは信頼できるアプリにのみ許可してください。\n\nすべてのデバイスログへのアクセスを許可しなかった場合も、このアプリはアプリ独自のログにアクセスできます。また、デバイスのメーカーもデバイスの一部のログや情報にアクセスできる可能性があります。"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"次回から表示しない"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"「<xliff:g id="APP_0">%1$s</xliff:g>」が「<xliff:g id="APP_2">%2$s</xliff:g>」のスライスの表示をリクエストしています"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"編集"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 5bd6aa6..090fc1f 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"თითის ანაბეჭდის აღების ოპერაცია გაუქმდა."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"თითის ანაბეჭდის ოპერაცია გააუქმა მომხმარებელმა."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ძალიან ბევრი მცდელობა იყო. სცადეთ მოგვიანებით."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"დაფიქსირდა მეტისმეტად ბევრი მცდელობა. თითის ანაბეჭდის სენსორი გათიშულია."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ხელახლა სცადეთ"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"თითის ანაბეჭდები რეგისტრირებული არ არის."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"არ არის მონიშნული"</string>
<string name="selected" msgid="6614607926197755875">"არჩეულია"</string>
<string name="not_selected" msgid="410652016565864475">"არ არის არჩეული"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{ერთი ვარსკვლავი სულ {max}-დან}other{# ვარსკვლავი სულ {max}-დან}}"</string>
<string name="in_progress" msgid="2149208189184319441">"მიმდინარეობს"</string>
<string name="whichApplication" msgid="5432266899591255759">"რა გამოვიყენოთ?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"მოქმედების %1$s-ის გამოყენებით დასრულება"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"რეგიონის პარამეტრები"</string>
<string name="search_language_hint" msgid="7004225294308793583">"აკრიფეთ ენის სახელი"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"რეკომენდებული"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"რეკომენდებული"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"შემოთავაზებული ენები"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"შეთავაზებული რეგიონები"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ყველა ენა"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"გსურთ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>-ს მიანიჭოთ მოწყობილობის ყველა ჟურნალზე წვდომა?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"ერთჯერადი წვდომის დაშვება"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"არ დაიშვას"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"მოწყობილობის ჟურნალში იწერება, რა ხდება ამ მოწყობილობაზე. აპებს შეუძლია ამ ჟურნალების გამოყენება პრობლემების აღმოსაჩენად და მოსაგვარებლად.\n\nზოგი ჟურნალი შეიძლება სენსიტიური ინფორმაციის მატარებელი იყოს, ამიტომაც მოწყობილობის ყველა ჟურნალზე წვდომა მხოლოდ სანდო აპებს მიანიჭეთ. \n\nთუ ამ აპს მოწყობილობის ყველა ჟურნალზე წვდომას არ მიანიჭებთ, მას მაინც ექნება წვდომა თქვენს ჟურნალებზე. თქვენი მოწყობილობის მწარმოებელს მაინც შეეძლება თქვენი მოწყობილობის ზოგიერთ ჟურნალსა თუ ინფორმაციაზე წვდომა."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"აღარ გამოჩნდეს"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>-ს სურს, გაჩვენოთ <xliff:g id="APP_2">%2$s</xliff:g>-ის ფრაგმენტები"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"რედაქტირება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 7552ed8..afd0bf8 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Саусақ ізі операциясынан бас тартылған."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Пайдаланушы саусақ ізі операциясынан бас тартты."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Талпыныстар тым көп. Кейінірек қайталап көріңіз."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Тым көп әрекет жасалды. Саусақ ізін оқу сканері өшірілді."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Әрекетті қайталаңыз."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Саусақ іздері тіркелмеген."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бұл құрылғыда саусақ ізін оқу сканері жоқ."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"белгіленбеген"</string>
<string name="selected" msgid="6614607926197755875">"таңдалған"</string>
<string name="not_selected" msgid="410652016565864475">"таңдалмаған"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1/{max} жұлдыз}other{#/{max} жұлдыз}}"</string>
<string name="in_progress" msgid="2149208189184319441">"орындалуда"</string>
<string name="whichApplication" msgid="5432266899591255759">"Әрекетті аяқтау"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Әрекетті %1$s қолданбасын пайдаланып аяқтау"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Аймақ параметрі"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Тіл атауын теріңіз"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Ұсынылған"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Ұсынылатын аймақтар"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Ұсынылған тілдер"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Ұсынылған аймақтар"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Барлық тілдер"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> қолданбасына барлық құрылғының журналын пайдалануға рұқсат берілсін бе?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Бір реттік пайдалану рұқсатын беру"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Рұқсат бермеу"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Журналдарға құрылғыда не болып жатқаны жазылады. Қолданбалар осы журналдарды қате тауып, түзету үшін пайдаланады.\n\nКейбір журналдарда құпия ақпарат болуы мүмкін. Сондықтан барлық құрылғының журналын пайдалану рұқсаты тек сенімді қолданбаларға берілуі керек. \n\nБұл қолданбаға барлық құрылғының журналын пайдалануға рұқсат бермесеңіз де, ол өзінің журналдарын пайдалана береді. Құрылғы өндірушісі де құрылғыдағы кейбір журналдарды немесе ақпаратты пайдалануы мүмкін."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Қайта көрсетілмесін"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> қолданбасы <xliff:g id="APP_2">%2$s</xliff:g> қолданбасының үзінділерін көрсеткісі келеді"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Өзгерту"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Белсенді қолданбаларды тексеру"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан телефон камерасын пайдалану мүмкін емес."</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан планшет камерасын пайдалану мүмкін емес."</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Трансляция кезінде мазмұнды көру мүмкін емес. Оның орнына телефоннан көріңіз."</string>
<string name="system_locale_title" msgid="711882686834677268">"Жүйенің әдепкі параметрі"</string>
<string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g>-КАРТА"</string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index c941e0a..a878eb3 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"បានបោះបង់ប្រតិបត្តិការស្នាមម្រាមដៃ។"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ប្រតិបត្តិការស្នាមម្រាមដៃត្រូវបានបោះបង់ដោយអ្នកប្រើប្រាស់។"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"ព្យាយាមចូលច្រើនដងពេកហើយ។ ឧបករណ៍ចាប់ស្នាមម្រាមដៃត្រូវបានបិទ។"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ព្យាយាមម្ដងទៀត។"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"មិនមានការចុះឈ្មោះស្នាមម្រាមដៃទេ។"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះមិនមានឧបករណ៍ចាប់ស្នាមម្រាមដៃទេ។"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"មិនបានធីក"</string>
<string name="selected" msgid="6614607926197755875">"បានជ្រើសរើស"</string>
<string name="not_selected" msgid="410652016565864475">"មិនបានជ្រើសរើសទេ"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{ផ្កាយមួយក្នុងចំណោមផ្កាយ {max}}other{ផ្កាយ # ក្នុងចំណោមផ្កាយ {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"កំពុងដំណើរការ"</string>
<string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់សកម្មភាពដោយប្រើ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់សកម្មភាពដោយប្រើ %1$s"</string>
@@ -1425,7 +1425,7 @@
<string name="ext_media_unmount_action" msgid="966992232088442745">"ដកចេញ"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"រុករក"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"លទ្ធផល Switch"</string>
- <string name="ext_media_missing_title" msgid="3209472091220515046">"បាត់ <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="ext_media_missing_title" msgid="3209472091220515046">"បាត់<xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_missing_message" msgid="4408988706227922909">"បញ្ចូលឧបករណ៍ម្តងទៀត"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"កំពុងផ្លាស់ទី <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"កំពុងផ្លាស់ទីទិន្នន័យ…"</string>
@@ -1565,7 +1565,7 @@
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="8490227947584914460">"ទំហំផ្ទុករួមខាងក្នុង"</string>
- <string name="storage_sd_card" msgid="3404740277075331881">"កាតអេសឌី"</string>
+ <string name="storage_sd_card" msgid="3404740277075331881">"កាត SD"</string>
<string name="storage_sd_card_label" msgid="7526153141147470509">"កាត SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"ឧបករណ៍ផ្ទុក USB"</string>
<string name="storage_usb_drive_label" msgid="6631740655876540521">"ឧបករណ៍ផ្ទុក USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ចំណូលចិត្តតំបន់"</string>
<string name="search_language_hint" msgid="7004225294308793583">"វាយបញ្ចូលឈ្មោះភាសា"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"បានណែនាំ"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"បានណែនាំ"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"ភាសាដែលបានណែនាំ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"តំបន់ដែលបានណែនាំ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ភាសាទាំងអស់"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"អនុញ្ញាតឱ្យ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់ឬ?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"អនុញ្ញាតឱ្យចូលប្រើម្ដង"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"មិនអនុញ្ញាត"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"កំណត់ហេតុឧបករណ៍កត់ត្រាអ្វីដែលកើតឡើងនៅលើឧបករណ៍របស់អ្នក។ កម្មវិធីអាចប្រើកំណត់ហេតុទាំងនេះដើម្បីស្វែងរក និងដោះស្រាយបញ្ហាបាន។\n\nកំណត់ហេតុមួយចំនួនអាចមានព័ត៌មានរសើប ដូច្នេះគួរអនុញ្ញាតឱ្យចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់សម្រាប់តែកម្មវិធីដែលអ្នកទុកចិត្តប៉ុណ្ណោះ។ \n\nប្រសិនបើអ្នកមិនអនុញ្ញាតឱ្យកម្មវិធីនេះចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់ទេ វានៅតែអាចចូលប្រើកំណត់ហេតុរបស់វាផ្ទាល់បាន។ ក្រុមហ៊ុនផលិតឧបករណ៍របស់អ្នកប្រហែលជានៅតែអាចចូលប្រើកំណត់ហេតុ ឬព័ត៌មានមួយចំនួននៅលើឧបករណ៍របស់អ្នកបានដដែល។"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"កុំបង្ហាញម្ដងទៀត"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ចង់បង្ហាញស្ថិតិប្រើប្រាស់របស់ <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"កែ"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ពិនិត្យមើលកម្មវិធីសកម្ម"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"មិនអាចចូលប្រើកាមេរ៉ាទូរសព្ទពី <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានទេ"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"មិនអាចចូលប្រើកាមេរ៉ាថេប្លេតពី <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានទេ"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"មិនអាចចូលប្រើប្រាស់ខ្លឹមសារនេះបានទេ ពេលផ្សាយ។ សូមសាកល្បងប្រើនៅលើទូរសព្ទរបស់អ្នកជំនួសវិញ។"</string>
<string name="system_locale_title" msgid="711882686834677268">"លំនាំដើមប្រព័ន្ធ"</string>
<string name="default_card_name" msgid="9198284935962911468">"កាត <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 31ec216..1bcc705 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ಬಳಕೆದಾರರು ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಿದ್ದಾರೆ."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ಹಲವಾರು ಪ್ರಯತ್ನಗಳು. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ಯಾವುದೇ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ"</string>
<string name="selected" msgid="6614607926197755875">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
<string name="not_selected" msgid="410652016565864475">"ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} ರಲ್ಲಿ ಒಂದು ನಕ್ಷತ್ರ}one{{max} ರಲ್ಲಿ # ನಕ್ಷತ್ರಗಳು}other{{max} ರಲ್ಲಿ # ನಕ್ಷತ್ರಗಳು}}"</string>
<string name="in_progress" msgid="2149208189184319441">"ಪ್ರಗತಿಯಲ್ಲಿದೆ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ಪ್ರದೇಶ ಪ್ರಾಶಸ್ತ್ಯ"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ಭಾಷೆ ಹೆಸರನ್ನು ಟೈಪ್ ಮಾಡಿ"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"ಸೂಚಿತ ಭಾಷೆ"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"ಸೂಚಿಸಲಾಗಿರುವುದು"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"ಸೂಚಿಸಲಾದ ಭಾಷೆಗಳು"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"ಸೂಚಿಸಲಾದ ಪ್ರದೇಶಗಳು"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ಎಲ್ಲಾ ಭಾಷೆಗಳು"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"ಎಲ್ಲಾ ಸಾಧನದ ಲಾಗ್ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ಗೆ ಅನುಮತಿಸುವುದೇ?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"ಒಂದು ಬಾರಿಯ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸಿ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ಅನುಮತಿಸಬೇಡಿ"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿನ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ಸಾಧನದ ಲಾಗ್ಗಳು ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತವೆ. ಸಮಸ್ಯೆಗಳನ್ನು ಪತ್ತೆಹಚ್ಚಲು ಮತ್ತು ಪರಿಹರಿಸಲು ಆ್ಯಪ್ಗಳು ಈ ಲಾಗ್ ಅನ್ನು ಬಳಸಬಹುದು.\n\nಕೆಲವು ಲಾಗ್ಗಳು ಸೂಕ್ಷ್ಮ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿರಬಹುದು, ಆದ್ದರಿಂದ ನಿಮ್ಮ ವಿಶ್ವಾಸಾರ್ಹ ಆ್ಯಪ್ಗಳಿಗೆ ಮಾತ್ರ ಸಾಧನದ ಎಲ್ಲಾ ಲಾಗ್ಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸಿ. \n\nಎಲ್ಲಾ ಸಾಧನ ಲಾಗ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ನೀವು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸದಿದ್ದರೆ, ಅದು ಆಗಲೂ ತನ್ನದೇ ಆದ ಲಾಗ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ನಿಮ್ಮ ಸಾಧನ ತಯಾರಕರಿಗೆ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿನ ಕೆಲವು ಲಾಗ್ಗಳು ಅಥವಾ ಮಾಹಿತಿಯನ್ನು ಪ್ರವೇಶಿಸಲು ಈಗಲೂ ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"ಮತ್ತೊಮ್ಮೆ ತೋರಿಸಬೇಡಿ"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_2">%2$s</xliff:g> ಸ್ಲೈಸ್ಗಳನ್ನು <xliff:g id="APP_0">%1$s</xliff:g> ತೋರಿಸಲು ಬಯಸಿದೆ"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ಎಡಿಟ್"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c17ab787..a08d2aa 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"지문 인식 작업이 취소되었습니다."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"사용자가 지문 인식 작업을 취소했습니다."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"시도 횟수가 너무 많습니다. 나중에 다시 시도하세요."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"시도 횟수가 너무 많습니다. 지문 센서가 사용 중지되었습니다."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"다시 시도해 보세요."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"등록된 지문이 없습니다."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string>
@@ -647,9 +648,9 @@
<string name="face_acquired_recalibrate" msgid="8724013080976469746">"얼굴을 다시 등록해 주세요."</string>
<string name="face_acquired_too_different" msgid="2520389515612972889">"얼굴을 인식할 수 없습니다. 다시 시도해 주세요."</string>
<string name="face_acquired_too_similar" msgid="8882920552674125694">"얼굴의 위치를 조금 변경해 주세요."</string>
- <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"휴대전화를 좀 더 똑바로 바라봐 주세요."</string>
- <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"휴대전화를 좀 더 똑바로 바라봐 주세요."</string>
- <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"휴대전화를 좀 더 똑바로 바라봐 주세요."</string>
+ <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"휴대전화를 좀 더\\n똑바로 바라봐 주세요."</string>
+ <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"휴대전화를 좀 더\\n똑바로 바라봐 주세요."</string>
+ <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"휴대전화를 좀 더\\n똑바로 바라봐 주세요."</string>
<string name="face_acquired_obscured" msgid="4917643294953326639">"얼굴이 가려지지 않도록 해 주세요."</string>
<string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"검은색 바를 포함한 화면 상단을 청소하세요."</string>
<!-- no translation found for face_acquired_dark_glasses_detected (5643703296620631986) -->
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"선택 안함"</string>
<string name="selected" msgid="6614607926197755875">"선택됨"</string>
<string name="not_selected" msgid="410652016565864475">"선택되지 않음"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{별 {max}개 중 1개}other{별 {max}개 중 #개}}"</string>
<string name="in_progress" msgid="2149208189184319441">"진행 중"</string>
<string name="whichApplication" msgid="5432266899591255759">"작업을 수행할 때 사용하는 애플리케이션"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s을(를) 사용하여 작업 완료"</string>
@@ -1422,7 +1422,7 @@
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"<xliff:g id="NAME">%s</xliff:g> 마운트 해제 중"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"외부 미디어를 제거하지 마세요."</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"설정"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"마운트 해제"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"꺼내기"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"둘러보기"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"출력 전환"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> 없음"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"지역 환경설정"</string>
<string name="search_language_hint" msgid="7004225294308793583">"언어 이름 입력"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"추천"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"추천 지역"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"추천 언어"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"추천 지역"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"모든 언어"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>에서 모든 기기 로그에 액세스하도록 허용하시겠습니까?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"일회성 액세스 허용"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"허용 안함"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"기기 로그에 기기에서 발생한 상황이 기록됩니다. 앱은 문제를 찾고 해결하는 데 이 로그를 사용할 수 있습니다.\n\n일부 로그는 민감한 정보를 포함할 수 있으므로 신뢰할 수 있는 앱만 모든 기기 로그에 액세스하도록 허용하세요. \n\n앱에 전체 기기 로그에 대한 액세스 권한을 부여하지 않아도 앱이 자체 로그에는 액세스할 수 있습니다. 기기 제조업체에서 일부 로그 또는 기기 내 정보에 액세스할 수도 있습니다."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"다시 표시 안함"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>에서 <xliff:g id="APP_2">%2$s</xliff:g>의 슬라이스를 표시하려고 합니다"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"수정"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"활성 상태의 앱 확인"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"사용자의 <xliff:g id="DEVICE">%1$s</xliff:g>에서 휴대전화 카메라에 액세스할 수 없습니다."</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"사용자의 <xliff:g id="DEVICE">%1$s</xliff:g>에서 태블릿 카메라에 액세스할 수 없습니다."</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"스트리밍 중에는 액세스할 수 없습니다. 대신 휴대전화에서 시도해 보세요."</string>
<string name="system_locale_title" msgid="711882686834677268">"시스템 기본값"</string>
<string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g> 카드"</string>
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 499b8b4..7572c4b 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Манжа изи иш-аракети жокко чыгарылды."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Манжа изи операциясын колдонуучу жокко чыгарды."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Аракеттер өтө көп болду. Бир аздан кийин кайталап көрүңүз."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Өтө көп жолу аракет жасадыңыз. Манжа изинин сенсору өчүрүлдү."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Кайра бир аракеттениңиз."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бир да манжа изи катталган эмес."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
<string name="selected" msgid="6614607926197755875">"тандалган"</string>
<string name="not_selected" msgid="410652016565864475">"тандалган жок"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} ичинен бир жылдыз}other{{max} ичинен # жылдыз}}"</string>
<string name="in_progress" msgid="2149208189184319441">"аткарылууда"</string>
<string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Чөлкөмдүк жөндөөлөр"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Тилди киргизиңиз"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Сунушталгандар"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Сунушталган"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Сунушталган тилдер"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Сунушталган региондор"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Бардык тилдер"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> колдонмосуна түзмөктөгү бардык таржымалдарды жеткиликтүү кыласызбы?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Бир жолу жеткиликтүү кылуу"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Жок"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Түзмөктө аткарылган бардык аракеттер түзмөктүн таржымалдарында сакталып калат. Колдонмолор бул таржымалдарды колдонуп, маселелерди оңдошот.\n\nАйрым таржымалдарда купуя маалымат болушу мүмкүн, андыктан түзмөктөгү бардык таржымалдарды ишенимдүү колдонмолорго гана пайдаланууга уруксат бериңиз. \n\nЭгер бул колдонмого түзмөктөгү айрым таржымалдарга кирүүгө тыюу салсаңыз, ал өзүнүн таржымалдарын пайдалана берет. Түзмөктү өндүрүүчү түзмөгүңүздөгү айрым таржымалдарды же маалыматты көрө берет."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Экинчи көрүнбөсүн"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> колдонмосу <xliff:g id="APP_2">%2$s</xliff:g> үлгүлөрүн көрсөткөнү жатат"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Түзөтүү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index bdbe79e..8bfe9e2 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ຍົກເລີກການດຳເນີນການລາຍນີ້ວມືແລ້ວ."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ຜູ້ໃຊ້ໄດ້ຍົກເລີກຄຳສັ່ງລາຍນິ້ວມືແລ້ວ."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ລອງໃໝ່ພາຍຫຼັງ."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ລະບົບປິດການເຮັດວຽກຂອງເຊັນເຊີລາຍນິ້ວມືແລ້ວ."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ລອງໃໝ່ອີກຄັ້ງ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ບໍ່ໄດ້ໝາຍຖືກ"</string>
<string name="selected" msgid="6614607926197755875">"ເລືອກແລ້ວ"</string>
<string name="not_selected" msgid="410652016565864475">"ບໍ່ໄດ້ເລືອກແລ້ວ"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{ໜຶ່ງດາວຈາກເຕັມ {max} ດາວ}other{# ດາວຈາກເຕັມ {max} ດາວ}}"</string>
<string name="in_progress" msgid="2149208189184319441">"ກຳລັງດຳເນີນການ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ດຳເນີນການໂດຍໃຊ້"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ສຳເລັດການດຳເນີນການໂດຍໃຊ້ %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ການຕັ້ງຄ່າພາກພື້ນ"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ພິມຊື່ພາສາ"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"ແນະນຳ"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"ແນະນຳ"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"ພາສາທີ່ແນະນຳ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"ພາກພື້ນທີ່ແນະນໍາ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ທຸກພາສາ"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"ອະນຸຍາດໃຫ້ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ເຂົ້າເຖິງບັນທຶກອຸປະກອນທັງໝົດບໍ?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"ອະນຸຍາດການເຂົ້າເຖິງແບບເທື່ອດຽວ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ບໍ່ອະນຸຍາດ"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"ບັນທຶກອຸປະກອນຈະບັນທຶກສິ່ງທີ່ເກີດຂຶ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ແອັບສາມາດໃຊ້ບັນທຶກເຫຼົ່ານີ້ເພື່ອຊອກຫາ ແລະ ແກ້ໄຂບັນຫາໄດ້.\n\nບັນທຶກບາງຢ່າງອາດມີຂໍ້ມູນລະອຽດອ່ອນ, ດັ່ງນັ້ນໃຫ້ອະນຸຍາດສະເພາະແອັບທີ່ທ່ານເຊື່ອຖືໃຫ້ເຂົ້າເຖິງບັນທຶກອຸປະກອນທັງໝົດເທົ່ານັ້ນ. \n\nຫາກທ່ານບໍ່ອະນຸຍາດແອັບນີ້ໃຫ້ເຂົ້າເຖິງບັນທຶກອຸປະກອນທັງໝົດ, ມັນຈະຍັງຄົງສາມາດເຂົ້າເຖິງບັນທຶກຂອງຕົວມັນເອງໄດ້ຢູ່. ຜູ້ຜະລິດອຸປະກອນຂອງທ່ານອາດຍັງຄົງສາມາດເຂົ້າເຖິງບັນທຶກ ຫຼື ຂໍ້ມູນບາງຢ່າງຢູ່ອຸປະກອນຂອງທ່ານໄດ້."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"ບໍ່ຕ້ອງສະແດງອີກ"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ຕ້ອງການສະແດງ <xliff:g id="APP_2">%2$s</xliff:g> ສະໄລ້"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ແກ້ໄຂ"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ກວດສອບແອັບທີ່ເຄື່ອນໄຫວ"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ບໍ່ສາມາດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງໂທລະສັບຈາກ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານໄດ້"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ບໍ່ສາມາດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງແທັບເລັດຈາກ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານໄດ້"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"ບໍ່ສາມາດເຂົ້າເຖິງເນື້ອຫານີ້ໄດ້ໃນຂະນະທີ່ຍັງສະຕຣີມຢູ່. ກະລຸນາລອງຢູ່ໂທລະສັບຂອງທ່ານແທນ."</string>
<string name="system_locale_title" msgid="711882686834677268">"ຄ່າເລີ່ມຕົ້ນຂອງລະບົບ"</string>
<string name="default_card_name" msgid="9198284935962911468">"ບັດ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index fdb86fc8..afa77e6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Piršto antspaudo operacija atšaukta."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Piršto antspaudo operaciją atšaukė naudotojas."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Per daug bandymų. Vėliau bandykite dar kartą."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Per daug bandymų. Piršto antspaudo jutiklis išjungtas."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Bandykite dar kartą."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neužregistruota jokių kontrolinių kodų."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"nepažymėta"</string>
<string name="selected" msgid="6614607926197755875">"pasirinkta"</string>
<string name="not_selected" msgid="410652016565864475">"nepasirinkta"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Viena žvaigždutė iš {max}}one{# žvaigždutė iš {max}}few{# žvaigždutės iš {max}}many{# žvaigždutės iš {max}}other{# žvaigždučių iš {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"vykdoma"</string>
<string name="whichApplication" msgid="5432266899591255759">"Užbaigti veiksmą naudojant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Užbaigti veiksmą naudojant %1$s"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Regiono nuostata"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Įveskite kalbos pav."</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Siūloma"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Siūloma"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Siūlomos kalbos"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Siūlomi regionai"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Visos kalbos"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Leisti „<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>“ pasiekti visus įrenginio žurnalus?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Leisti vienkartinę prieigą"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Neleisti"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Įrenginyje įrašoma, kas įvyksta jūsų įrenginyje. Programos gali naudoti šiuos žurnalus, kad surastų ir išspręstų problemas.\n\nKai kuriuose žurnaluose gali būti neskelbtinos informacijos, todėl visus įrenginio žurnalus leiskite pasiekti tik programoms, kuriomis pasitikite. \n\nJei neleisite šiai programai pasiekti visų įrenginio žurnalų, ji vis tiek galės pasiekti savo žurnalus. Įrenginio gamintojui vis tiek gali būti leidžiama pasiekti tam tikrus žurnalus ar informaciją jūsų įrenginyje."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Daugiau neberodyti"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"„<xliff:g id="APP_0">%1$s</xliff:g>“ nori rodyti „<xliff:g id="APP_2">%2$s</xliff:g>“ fragmentus"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Redaguoti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 7b8fae9..d44c435 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -606,7 +606,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Nospieduma darbība neizdevās."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Lietotājs atcēla pirksta nospieduma darbību."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Pārāk daudz mēģinājumu. Vēlāk mēģiniet vēlreiz."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Pārāk daudz mēģinājumu. Pirksta nospieduma sensors atspējots."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Mēģiniet vēlreiz."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nav reģistrēts neviens pirksta nospiedums."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
@@ -1168,8 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"nav atzīmēts"</string>
<string name="selected" msgid="6614607926197755875">"atlasīts"</string>
<string name="not_selected" msgid="410652016565864475">"nav atlasīts"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Viena zvaigznīte no {max}}zero{# zvaigznīšu no {max}}one{# zvaigznīte no {max}}other{# zvaigznītes no {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"notiek apstrāde"</string>
<string name="whichApplication" msgid="5432266899591255759">"Izvēlieties lietotni"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Pabeigt darbību, izmantojot %1$s"</string>
@@ -1926,8 +1926,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Reģiona preference"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Ierakstiet valodas nosaukumu"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Ieteiktās"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Ieteiktie"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Ieteiktās valodas"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Ieteiktie reģioni"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Visas valodas"</string>
@@ -2052,8 +2051,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Vai atļaujat lietotnei <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> piekļūt visiem ierīces žurnāliem?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Atļaut vienreizēju piekļuvi"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Neatļaut"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Ierīces žurnālos tiek reģistrēti ierīces procesi un notikumi. Lietotņu izstrādātāji var izmantot šos žurnālus, lai atrastu un izlabotu problēmas savās lietotnēs.\n\nDažos žurnālos var būt ietverta sensitīva informācija, tāpēc atļaujiet tikai uzticamām lietotnēm piekļūt visiem ierīces žurnāliem. \n\nJa neatļausiet šai lietotnei piekļūt visiem ierīces žurnāliem, lietotnes izstrādātājs joprojām varēs piekļūt pašas lietotnes žurnāliem. Iespējams, ierīces ražotājs joprojām varēs piekļūt noteiktiem žurnāliem vai informācijai jūsu ierīcē."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Vairs nerādīt"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Lietotne <xliff:g id="APP_0">%1$s</xliff:g> vēlas rādīt lietotnes <xliff:g id="APP_2">%2$s</xliff:g> sadaļas"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Rediģēt"</string>
@@ -2294,8 +2292,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Pārbaudiet aktīvās lietotnes"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nevar piekļūt tālruņa kamerai no jūsu ierīces (<xliff:g id="DEVICE">%1$s</xliff:g>)."</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nevar piekļūt planšetdatora kamerai no jūsu ierīces (<xliff:g id="DEVICE">%1$s</xliff:g>)."</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Straumēšanas laikā nevar piekļūt šim saturam. Mēģiniet tam piekļūt savā tālrunī."</string>
<string name="system_locale_title" msgid="711882686834677268">"Sistēmas noklusējums"</string>
<string name="default_card_name" msgid="9198284935962911468">"KARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index e49d500..e74bc07 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцијата со отпечаток се откажа."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисникот ја откажа потврдата со отпечаток."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Премногу обиди. Обидете се повторно подоцна."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Премногу обиди. Сензорот за отпечатоци е оневозможен."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Обидете се повторно."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Не се запишани отпечатоци."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
<string name="selected" msgid="6614607926197755875">"избрано"</string>
<string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Една ѕвезда од {max}}one{# ѕвезда од {max}}other{# ѕвезди од {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"во тек"</string>
<string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
@@ -1565,8 +1565,8 @@
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="8490227947584914460">"Внатрешен споделен капацитет"</string>
- <string name="storage_sd_card" msgid="3404740277075331881">"СД картичка"</string>
- <string name="storage_sd_card_label" msgid="7526153141147470509">"<xliff:g id="MANUFACTURER">%s</xliff:g> СД-картичка"</string>
+ <string name="storage_sd_card" msgid="3404740277075331881">"SD-картичка"</string>
+ <string name="storage_sd_card_label" msgid="7526153141147470509">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD-картичка"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"USB-меморија"</string>
<string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB-меморија"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB меморија"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Претпочитувања за регион"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Внесете име на јазик"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Предложени"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Предложени"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Предложени јазици"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Предложени региони"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Сите јазици"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Да се дозволи <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> да пристапува до целата евиденција на уредот?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Дозволи еднократен пристап"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дозволувај"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Дневниците за евиденција на уредот снимаат што се случува на вашиот уред. Апликациите може да ги користат овие дневници за евиденција за да наоѓаат и поправаат проблеми.\n\nНекои дневници за евиденција може да содржат чувствителни податоци, па затоа дозволете им пристап до сите дневници за евиденција на уредот само на апликациите во кои имате доверба. \n\nАко не ѝ дозволите на апликацијава да пристапува до сите дневници за евиденција на уредот, таа сепак ќе може да пристапува до сопствените дневници за евиденција. Производителот на вашиот уред можеби сепак ќе може да пристапува до некои дневници за евиденција или податоци на уредот."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Не прикажувај повторно"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> сака да прикажува делови од <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Измени"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Проверете ги активните апликации"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не може да се пристапи до камерата на вашиот телефон од <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не може да се пристапи до камерата на вашиот таблет од <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"До ова не може да се пристапи при стриминг. Наместо тоа, пробајте на вашиот телефон."</string>
<string name="system_locale_title" msgid="711882686834677268">"Стандардно за системот"</string>
<string name="default_card_name" msgid="9198284935962911468">"КАРТИЧКА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index d03f04e..70b706a 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ഉപയോക്താവ് റദ്ദാക്കിയ ഫിംഗർപ്രിന്റ് പ്രവർത്തനം."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"നിരവധി തവണ ശ്രമിച്ചതിനാൽ, ഫിംഗർപ്രിന്റ് സെൻസർ പ്രവർത്തനരഹിതമായി."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"വീണ്ടും ശ്രമിക്കൂ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"വിരലടയാളങ്ങൾ എൻറോൾ ചെയ്തിട്ടില്ല."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"പരിശോധിക്കാത്തത്"</string>
<string name="selected" msgid="6614607926197755875">"തിരഞ്ഞെടുത്തു"</string>
<string name="not_selected" msgid="410652016565864475">"തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max}-ൽ ഒരു നക്ഷത്ര ചിഹ്നം}other{{max}-ൽ # നക്ഷത്ര ചിഹ്നം}}"</string>
<string name="in_progress" msgid="2149208189184319441">"പുരോഗതിയിലാണ്"</string>
<string name="whichApplication" msgid="5432266899591255759">"പൂർണ്ണമായ പ്രവർത്തനം ഉപയോഗിക്കുന്നു"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ഉപയോഗിച്ച് പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
@@ -1422,7 +1422,7 @@
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"<xliff:g id="NAME">%s</xliff:g> ഒഴിവാക്കുന്നു"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"നീക്കം ചെയ്യരുത്"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"സജ്ജമാക്കുക"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"നിരസിക്കുക"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"ഒഴിവാക്കുക"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"അടുത്തറിയുക"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"ഔട്ട്പുട്ട് മാറുക"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> കാണുന്നില്ല"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"മേഖലാ മുൻഗണന"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ഭാഷ ടൈപ്പ് ചെയ്യുക"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"നിര്ദ്ദേശിച്ചത്"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"നിർദ്ദേശിച്ചവ"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"നിർദ്ദേശിച്ച ഭാഷകൾ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"നിർദ്ദേശിച്ച പ്രദേശങ്ങൾ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"എല്ലാ ഭാഷകളും"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"എല്ലാ ഉപകരണ ലോഗുകളും ആക്സസ് ചെയ്യാൻ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"ഒറ്റത്തവണ ആക്സസ് അനുവദിക്കുക"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"അനുവദിക്കരുത്"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"ഉപകരണ ലോഗുകൾ നിങ്ങളുടെ ഉപകരണത്തിൽ എന്തൊക്കെയാണ് സംഭവിക്കുന്നതെന്ന് റെക്കോർഡ് ചെയ്യുന്നു. പ്രശ്നങ്ങൾ കണ്ടെത്തി പരിഹരിക്കുന്നതിന് ആപ്പുകൾക്ക് ഈ ലോഗുകൾ ഉപയോഗിക്കാൻ കഴിയും.\n\nചില ലോഗുകളിൽ സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട വിവരങ്ങൾ അടങ്ങിയിരിക്കാൻ സാധ്യതയുള്ളതിനാൽ, എല്ലാ ഉപകരണ ലോഗുകളും ആക്സസ് ചെയ്യാനുള്ള അനുമതി നിങ്ങൾക്ക് വിശ്വാസമുള്ള ആപ്പുകൾക്ക് മാത്രം നൽകുക. \n\nഎല്ലാ ഉപകരണ ലോഗുകളും ആക്സസ് ചെയ്യാനുള്ള അനുവാദം നൽകിയില്ലെങ്കിലും, ഈ ആപ്പിന് അതിന്റെ സ്വന്തം ലോഗുകൾ ആക്സസ് ചെയ്യാനാകും. നിങ്ങളുടെ ഉപകരണ നിർമ്മാതാവിന് തുടർന്നും നിങ്ങളുടെ ഉപകരണത്തിലെ ചില ലോഗുകളോ വിവരങ്ങളോ ആക്സസ് ചെയ്യാനായേക്കും."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"വീണ്ടും കാണിക്കരുത്"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_2">%2$s</xliff:g> സ്ലൈസുകൾ കാണിക്കാൻ <xliff:g id="APP_0">%1$s</xliff:g> താൽപ്പര്യപ്പെടുന്നു"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"എഡിറ്റ് ചെയ്യുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 1f4eaf9..4e50f56 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Хурууны хээний бүртгэл амжилтгүй боллоо."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Хэрэглэгч хурууны хээний баталгаажуулалтыг цуцалсан байна."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Хэтэрхий олон оролдлоо. Түр хүлээгээд дахин оролдоно уу."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Хэт олон удаа оролдсон тул хурууны хээ мэдрэгчийг идэвхгүй болголоо."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Дахин оролдно уу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бүртгүүлсэн хурууны хээ алга."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"тэмдэглээгүй"</string>
<string name="selected" msgid="6614607926197755875">"сонгосон"</string>
<string name="not_selected" msgid="410652016565864475">"сонгоогүй"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max}-с нэг од}other{{max}-с # од}}"</string>
<string name="in_progress" msgid="2149208189184319441">"үргэлжилж байна"</string>
<string name="whichApplication" msgid="5432266899591255759">"Үйлдлийг дуусгах"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ашиглан үйлдлийг гүйцээх"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Бүс нутгийн тохиргоо"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Улсын хэлийг бичнэ үү"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Санал болгосон"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Санал болгосон"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Санал болгосон хэл"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Санал болгосон бүс нутгууд"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Бүх хэл"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>-д төхөөрөмжийн бүх логт хандахыг зөвшөөрөх үү?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Нэг удаагийн хандалтыг зөвшөөрнө үү"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Бүү зөвшөөр"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Төхөөрөмжийн лог нь таны төхөөрөмж дээр юу болж байгааг бичдэг. Аппууд эдгээр логийг асуудлыг олох болон засахад ашиглах боломжтой.\n\nЗарим лог эмзэг мэдээлэл агуулж байж магадгүй тул та зөвхөн итгэдэг аппууддаа төхөөрөмжийн бүх логт хандахыг зөвшөөрнө үү. \n\nХэрэв та энэ аппад төхөөрөмжийн бүх логт хандахыг зөвшөөрөхгүй бол энэ нь өөрийн логт хандах боломжтой хэвээр байх болно. Tаны төхөөрөмж үйлдвэрлэгч таны төхөөрөмж дээрх зарим лог эсвэл мэдээлэлд хандах боломжтой хэвээр байж магадгүй."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Дахиж бүү харуул"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g>-н хэсгүүдийг (slices) харуулах хүсэлтэй байна"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Засах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index a09a529..371f776 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"खूप प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"खूप प्रयत्न करून झाले. फिंगरप्रिंट सेन्सर बंद आहे."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"तपासले नाही"</string>
<string name="selected" msgid="6614607926197755875">"निवडला"</string>
<string name="not_selected" msgid="410652016565864475">"निवडला नाही"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} पैकी एक तारा}other{{max} पैकी # तारे}}"</string>
<string name="in_progress" msgid="2149208189184319441">"प्रगतीपथावर आहे"</string>
<string name="whichApplication" msgid="5432266899591255759">"याचा वापर करून क्रिया पूर्ण करा"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s वापरून क्रिया पूर्ण करा"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"प्रदेश प्राधान्य"</string>
<string name="search_language_hint" msgid="7004225294308793583">"भाषा नाव टाइप करा"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"सुचवलेल्या भाषा"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"सुचवलेले"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"सुचवलेल्या भाषा"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"सुचवलेले प्रदेश"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"सर्व भाषा"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ला सर्व डिव्हाइस लॉग अॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"एक वेळ अॅक्सेसची अनुमती द्या"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"अनुमती देऊ नका"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"तुमच्या डिव्हाइसवर काय होते ते डिव्हाइस लॉग रेकॉर्ड करते. समस्या शोधण्यासाठी आणि त्यांचे निराकरण करण्याकरिता ॲप्स हे लॉग वापरू शकतात.\n\nकाही लॉगमध्ये संवेदनशील माहिती असू शकते, त्यामुळे फक्त तुमचा विश्वास असलेल्या ॲप्सना सर्व डिव्हाइस लॉग अॅक्सेस करण्याची अनुमती द्या. \n\nतुम्ही या ॲपला सर्व डिव्हाइस लॉग अॅक्सेस करण्याची अनुमती न दिल्यास, ते तरीही त्याचा स्वतःचा लॉग अॅक्सेस करू शकते. तुमच्या डिव्हाइसचा उत्पादक तरीही काही लॉग किंवा तुमच्या डिव्हाइसवरील माहिती अॅक्सेस करू शकतो."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"पुन्हा दाखवू नका"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवायचे आहेत"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"संपादित करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 6e9f493..2179308 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Pengendalian cap jari dibatalkan."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Pengendalian cap jari dibatalkan oleh pengguna."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Terlalu banyak percubaan. Penderia cap jari dilumpuhkan."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Cuba lagi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tiada cap jari didaftarkan."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"tidak ditandai"</string>
<string name="selected" msgid="6614607926197755875">"dipilih"</string>
<string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Satu daripada {max} bintang}other{# daripada {max} bintang}}"</string>
<string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Pilihan wilayah"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Taipkan nama bahasa"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Dicadangkan"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Dicadangkan"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Bahasa yang dicadangkan"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Rantau yang disyorkan"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Semua bahasa"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Benarkan <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> mengakses semua log peranti?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Benarkan akses satu kali"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Jangan benarkan"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Log peranti merekodkan perkara yang berlaku pada peranti anda. Apl dapat menggunakan log ini untuk menemukan dan membetulkan isu.\n\nSesetengah log mungkin mengandungi maklumat sensitif, jadi benarkan apl yang anda percaya sahaja untuk mengakses semua log peranti. \n\nJika anda tidak membenarkan apl ini mengakses semua log peranti, apl masih boleh mengakses log sendiri. Pengilang peranti anda mungkin masih dapat mengakses sesetengah log atau maklumat pada peranti anda."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Jangan tunjuk lagi"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> mahu menunjukkan <xliff:g id="APP_2">%2$s</xliff:g> hirisan"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Semak apl aktif"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Tidak dapat mengakses kamera telefon daripada <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Tidak dapat mengakses kamera tablet daripada <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Kandungan ini tidak boleh diakses semasa penstriman. Cuba pada telefon anda."</string>
<string name="system_locale_title" msgid="711882686834677268">"Lalai sistem"</string>
<string name="default_card_name" msgid="9198284935962911468">"KAD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index c038ed7..ebaa967 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"လက်ဗွေယူခြင်း ပယ်ဖျက်လိုက်သည်။"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"လက်ဗွေဖြင့် အထောက်အထားစိစစ်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်ထားသည်။"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ကြိုးစာမှု အကြိမ်များနေ၏။ နောက်မှ ထပ်မံကြိုးစားပါ။"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"အကြိမ်အရေအတွက် အလွန်များနေပါပြီ။ လက်ဗွေအာရုံခံကိရိယာ ပိတ်ထားပါသည်။"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ပြန်ကြိုးစားပါ"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
<string name="selected" msgid="6614607926197755875">"ရွေးချယ်ထားသည်"</string>
<string name="not_selected" msgid="410652016565864475">"ရွေးချယ်မထားပါ"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} ပွင့်အနက် ကြယ်တစ်ပွင့်}other{{max} ပွင့်အနက် ကြယ် # ပွင့်}}"</string>
<string name="in_progress" msgid="2149208189184319441">"ဆောင်ရွက်နေသည်"</string>
<string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
@@ -1422,11 +1422,11 @@
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"<xliff:g id="NAME">%s</xliff:g> ကို ထုတ်နေသည်"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"မဖယ်ရှားပါနှင့်"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"သတ်မှတ်ရန်"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"ထုတ်မည်"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"ထုတ်ရန်"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"စူးစမ်းရန်"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"မီဒီယာအထွက် ပြောင်းရန်"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> ပျောက်နေသည်"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"စက်ပစ္စည်းကို ထပ်မံထည့်သွင်းပါ"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"ကတ်ကို ထပ်မံထည့်သွင်းပါ"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"<xliff:g id="NAME">%s</xliff:g> ရွှေ့နေစဉ်"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"ဒေတာများ ရွှေ့နေစဉ်"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"အကြောင်းအရာ လွှဲပြောင်းပြီးပါပြီ"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ဒေသရွေးချယ်မှု"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ဘာသာစကားအမည် ထည့်ပါ"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"အကြံပြုထားသည်"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"အကြံပြုထားသည်"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"အကြံပြုထားသည့် ဘာသာစကားများ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"အကြံပြုထားသည့် ဒေသများ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ဘာသာစကားအားလုံး"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ကို စက်မှတ်တမ်းအားလုံး သုံးခွင့်ပြုမလား။"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"တစ်ခါသုံး ဝင်ခွင့်ပေးရန်"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ခွင့်မပြုပါ"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"သင့်စက်ရှိ အဖြစ်အပျက်များကို စက်မှတ်တမ်းများက မှတ်တမ်းတင်သည်။ အက်ပ်များက ပြဿနာများ ရှာဖွေပြီးဖြေရှင်းရန် ဤမှတ်တမ်းများကို သုံးနိုင်သည်။\n\nအချို့မှတ်တမ်းများတွင် သတိထားရမည့်အချက်အလက်များ ပါဝင်နိုင်သဖြင့် စက်မှတ်တမ်းအားလုံးကို ယုံကြည်ရသည့် အက်ပ်များကိုသာ သုံးခွင့်ပြုပါ။ \n\nဤအက်ပ်ကို စက်မှတ်တမ်းအားလုံး သုံးခွင့်မပြုသော်လည်း ၎င်းက ၎င်း၏ကိုယ်ပိုင်မှတ်တမ်းကို သုံးနိုင်ဆဲဖြစ်သည်။ သင့်စက်ရှိ အချို့မှတ်တမ်းများ (သို့) အချက်အလက်များကို သင့်စက်ထုတ်လုပ်သူက သုံးနိုင်ပါသေးသည်။"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"နောက်ထပ်မပြပါနှင့်"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> သည် <xliff:g id="APP_2">%2$s</xliff:g> ၏အချပ်များကို ပြသလိုသည်"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"တည်းဖြတ်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 0aa7cdb..120c08a 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrykk-operasjonen ble avbrutt."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrykk-operasjonen ble avbrutt av brukeren."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"For mange forsøk. Prøv på nytt senere."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"For mange forsøk. Fingeravtrykkssensoren er slått av."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Prøv på nytt."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ingen fingeravtrykk er registrert."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ikke avmerket"</string>
<string name="selected" msgid="6614607926197755875">"valgt"</string>
<string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 stjerne av {max}}other{# stjerner av {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"pågår"</string>
<string name="whichApplication" msgid="5432266899591255759">"Fullfør med"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Fullfør handlingen med %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Regionsinnstilling"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Skriv inn språknavn"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Foreslått"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Foreslått"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Foreslåtte språk"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Foreslåtte regioner"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Alle språk"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Vil du gi <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> tilgang til alle enhetslogger?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Gi éngangstilgang"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ikke tillat"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Enhetslogger registrerer det som skjer på enheten din. Apper kan bruke disse loggene til å finne og løse problemer.\n\nNoen logger kan inneholde sensitiv informasjon, så du bør bare gi tilgang til alle enhetslogger til apper du stoler på. \n\nHvis du ikke gir denne appen tilgang til alle enhetslogger, har den fortsatt tilgang til sine egne logger. Enhetsprodusenten kan fortsatt ha tilgang til visse logger eller noe informasjon på enheten din."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ikke vis igjen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vil vise <xliff:g id="APP_2">%2$s</xliff:g>-utsnitt"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Endre"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 3f653d9..d8f8077 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिन्ट सञ्चालन रद्द गरियो।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"धेरै प्रयासहरू। केहि समय पछि पुन: प्रयास गर्नुहोला"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"अत्यन्त धेरै प्रयासहरू। फिंगरप्रिन्ट सेन्सरलाई असक्षम पारियो।"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन: प्रयास गर्नुहोला।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो डिभाइसमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string>
<string name="selected" msgid="6614607926197755875">"चयन गरियो"</string>
<string name="not_selected" msgid="410652016565864475">"चयन गरिएन"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} मा एक तारा}other{{max} मा # तारा}}"</string>
<string name="in_progress" msgid="2149208189184319441">"जारी छ"</string>
<string name="whichApplication" msgid="5432266899591255759">"प्रयोग गरेर कारबाही पुरा गर्नुहोस्"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s"</string>
@@ -1426,7 +1426,7 @@
<string name="ext_media_browse_action" msgid="344865351947079139">"अन्वेषण गर्नुहोस्"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"आउटपुट बदल्नुहोस्"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> हराइरहेको"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"यन्त्र फेरि घुसाउनुहोस्"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"डिभाइस फेरि हाल्नुहोस्"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"<xliff:g id="NAME">%s</xliff:g> सार्दै"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"डेटा सार्दै..."</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"सामग्री स्थानान्तरण गरियो"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"क्षेत्रको प्राथमिकता"</string>
<string name="search_language_hint" msgid="7004225294308793583">"भाषाको नाम टाइप गर्नुहोस्"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"सिफारिस गरिएको"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"सिफारिस गरिएको"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"सिफारिस गरिएका भाषाहरू"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"सिफारिस गरिएका क्षेत्रहरू"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"सम्पूर्ण भाषाहरू"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> लाई डिभाइसका सबै लग हेर्ने अनुमति दिने हो?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"एक पटक प्रयोग गर्ने अनुमति दिनुहोस्"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"अनुमति नदिनुहोस्"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"डिभाइसका लगले तपाईंको डिभाइसमा भएका विभिन्न गतिविधिको अभिलेख राख्छ। एपहरू यी लगका आधारमा समस्या पत्ता लगाउन र तिनको समाधान गर्न सक्छन्।\n\nकेही लगहरूमा संवेदनशील जानकारी समावेश हुन सक्ने भएकाले आफूले भरोसा गर्ने एपलाई मात्र डिभाइसका सबै लग हेर्ने अनुमति दिनुहोस्। \n\nतपाईंले यो एपलाई डिभाइसका सबै लग हेर्ने अनुमति दिनुभएन भने पनि यसले आफ्नै लग भने हेर्न सक्छ। तपाईंको डिभाइसको उत्पादकले पनि तपाईंको डिभाइसमा भएका केही लग वा जानकारी हेर्न सक्ने सम्भावना हुन्छ।"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"फेरि नदेखाइयोस्"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ले <xliff:g id="APP_2">%2$s</xliff:g> का स्लाइसहरू देखाउन चाहन्छ"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"सम्पादन गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index bcf0c3c..3ff13f6 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukbewerking geannuleerd."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukverificatie geannuleerd door gebruiker."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Te veel pogingen. Probeer het later opnieuw."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Te veel pogingen. Vingerafdruksensor staat uit."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probeer het opnieuw."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukken geregistreerd."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"niet aangevinkt"</string>
<string name="selected" msgid="6614607926197755875">"geselecteerd"</string>
<string name="not_selected" msgid="410652016565864475">"niet geselecteerd"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Eén ster van {max}}other{# sterren van {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"bezig"</string>
<string name="whichApplication" msgid="5432266899591255759">"Actie voltooien met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Actie voltooien via %1$s"</string>
@@ -1426,7 +1426,7 @@
<string name="ext_media_browse_action" msgid="344865351947079139">"Verkennen"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Uitvoer wijzigen"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> ontbreekt"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"Voer apparaat opnieuw in"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"Voer het apparaat opnieuw in"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"<xliff:g id="NAME">%s</xliff:g> verplaatsen"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Gegevens verplaatsen"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"Contentoverdracht is voltooid"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Regiovoorkeur"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Typ de naam van een taal"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Voorgesteld"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Voorgesteld"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Voorgestelde talen"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Voorgestelde regio\'s"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Alle talen"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> toegang geven tot alle apparaatlogboeken?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Eenmalige toegang toestaan"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Niet toestaan"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Apparaatlogboeken leggen vast wat er op je apparaat gebeurt. Apps kunnen deze logboeken gebruiken om problemen op te sporen en te verhelpen.\n\nSommige logboeken kunnen gevoelige informatie bevatten, dus geef alleen apps die je vertrouwt toegang tot alle apparaatlogboeken. \n\nAls je deze app geen toegang tot alle apparaatlogboeken geeft, heeft de app nog wel toegang tot de eigen logboeken. De fabrikant van je apparaat heeft misschien nog steeds toegang tot bepaalde logboeken of informatie op je apparaat."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Niet opnieuw tonen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wil segmenten van <xliff:g id="APP_2">%2$s</xliff:g> tonen"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Bewerken"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index a77ea2f..29391d6 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -211,7 +211,7 @@
<string name="silent_mode" msgid="8796112363642579333">"ସାଇଲେଣ୍ଟ ମୋଡ୍"</string>
<string name="turn_on_radio" msgid="2961717788170634233">"ୱେୟାରଲେସ୍କୁ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="turn_off_radio" msgid="7222573978109933360">"ୱେୟାରଲେସ୍କୁ ବନ୍ଦ କରନ୍ତୁ"</string>
- <string name="screen_lock" msgid="2072642720826409809">"ସ୍କ୍ରୀନ୍ ଲକ୍"</string>
+ <string name="screen_lock" msgid="2072642720826409809">"ସ୍କ୍ରିନ ଲକ"</string>
<string name="power_off" msgid="4111692782492232778">"ପାୱାର ବନ୍ଦ ଅଛି"</string>
<string name="silent_mode_silent" msgid="5079789070221150912">"ରିଙ୍ଗର୍ ଅଫ୍ ଅଛି"</string>
<string name="silent_mode_vibrate" msgid="8821830448369552678">"ରିଙ୍ଗର୍ କମ୍ପନ"</string>
@@ -235,7 +235,7 @@
<string name="global_actions" product="tablet" msgid="4412132498517933867">"ଟାବଲେଟ ବିକଳ୍ପ"</string>
<string name="global_actions" product="tv" msgid="3871763739487450369">"Android TVର ବିକଳ୍ପଗୁଡ଼ିକ"</string>
<string name="global_actions" product="default" msgid="6410072189971495460">"ଫୋନ ବିକଳ୍ପ"</string>
- <string name="global_action_lock" msgid="6949357274257655383">"ସ୍କ୍ରୀନ୍ ଲକ୍"</string>
+ <string name="global_action_lock" msgid="6949357274257655383">"ସ୍କ୍ରିନ ଲକ"</string>
<string name="global_action_power_off" msgid="4404936470711393203">"ପାୱାର ବନ୍ଦ ଅଛି"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"ପାୱାର"</string>
<string name="global_action_restart" msgid="4678451019561687074">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string>
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରାଗଲା।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ଉପଯୋଗକର୍ତ୍ତା ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରିଛନ୍ତି।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ବହୁତ ପ୍ରୟାସ କରାଗଲା। ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"ବହୁଥର ପ୍ରୟାସ କରିଛନ୍ତି। ଟିପଚିହ୍ନ ସେନ୍ସର୍ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍ରେ ଟିପଚିହ୍ନ ସେନ୍ସର୍ ନାହିଁ।"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ଯାଞ୍ଚ ହୋଇନାହିଁ"</string>
<string name="selected" msgid="6614607926197755875">"ଚୟନ କରାଯାଇଛି"</string>
<string name="not_selected" msgid="410652016565864475">"ଚୟନ କରାଯାଇନାହିଁ"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max}ଟିରୁ ଗୋଟିଏ ଷ୍ଟାର}other{{max}ଟିରୁ #ଟି ଷ୍ଟାର}}"</string>
<string name="in_progress" msgid="2149208189184319441">"ଚାଲୁଅଛି"</string>
<string name="whichApplication" msgid="5432266899591255759">"ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ପସନ୍ଦର ଅଞ୍ଚଳ"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ଭାଷାର ନାମ ଟାଇପ୍ କରନ୍ତୁ"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"ପ୍ରସ୍ତାବିତ"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"ପ୍ରସ୍ତାବିତ"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"ପ୍ରସ୍ତାବିତ ଭାଷାଗୁଡ଼ିକ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"ପ୍ରସ୍ତାବିତ ଅଞ୍ଚଳଗୁଡ଼ିକ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ସମସ୍ତ ଭାଷା"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"ଗୋଟିଏ-ଥର ଆକ୍ସେସ ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"ଆପଣଙ୍କ ଡିଭାଇସରେ ଯାହା ହୁଏ ତାହା ଡିଭାଇସ ଲଗଗୁଡ଼ିକ ରେକର୍ଡ କରେ। ସମସ୍ୟାଗୁଡ଼ିକୁ ଖୋଜି ସମାଧାନ କରିବାକୁ ଆପ୍ସ ଏହି ଲଗଗୁଡ଼ିକୁ ବ୍ୟବହାର କରିପାରିବ।\n\nକିଛି ଲଗରେ ସମ୍ବେଦନଶୀଳ ସୂଚନା ଥାଇପାରେ, ତେଣୁ ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଆପଣ ବିଶ୍ୱାସ କରୁଥିବା ଆପ୍ସକୁ ହିଁ ଅନୁମତି ଦିଅନ୍ତୁ। \n\nଯଦି ଆପଣ ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତି ନାହିଁ, ତେବେ ବି ଏହା ନିଜର ଡିଭାଇସ ଲଗଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିପାରିବ। ଆପଣଙ୍କ ଡିଭାଇସର ନିର୍ମାତା ଏବେ ବି ଆପଣଙ୍କର ଡିଭାଇସରେ କିଛି ଲଗ କିମ୍ବା ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ସକ୍ଷମ ହୋଇପାରନ୍ତି।"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"ପୁଣି ଦେଖାନ୍ତୁ ନାହିଁ"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍କୁ ଦେଖାଇବା ପାଇଁ ଚାହେଁ"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ଏଡିଟ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 7cf2acc..0433cf4 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ. ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ।"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤੇ ਗਏ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ।"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ਨਿਸ਼ਾਨਬੱਧ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="selected" msgid="6614607926197755875">"ਚੁਣਿਆ ਗਿਆ"</string>
<string name="not_selected" msgid="410652016565864475">"ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} ਵਿੱਚੋਂ ਇੱਕ ਤਾਰਾ}one{{max} ਵਿੱਚੋਂ # ਤਾਰਾ}other{{max} ਵਿੱਚੋਂ # ਤਾਰੇ}}"</string>
<string name="in_progress" msgid="2149208189184319441">"ਜਾਰੀ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ਇਸਨੂੰ ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ਖੇਤਰ ਤਰਜੀਹ"</string>
<string name="search_language_hint" msgid="7004225294308793583">"ਭਾਸ਼ਾ ਦਾ ਨਾਮ ਟਾਈਪ ਕਰੋ"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"ਸੁਝਾਈਆਂ ਗਈਆਂ"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"ਸੁਝਾਈਆਂ ਗਈਆਂ"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"ਸੁਝਾਈਆਂ ਗਈਆਂ ਭਾਸ਼ਾਵਾਂ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"ਸੁਝਾਏ ਗਏ ਖੇਤਰ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ਸਾਰੀਆਂ ਭਾਸ਼ਾਵਾਂ"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"ਕੀ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ਨੂੰ ਸਾਰੇ ਡੀਵਾਈਸ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"ਇੱਕ-ਵਾਰ ਲਈ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ਆਗਿਆ ਨਾ ਦਿਓ"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"ਡੀਵਾਈਸ ਲੌਗਾਂ ਵਿੱਚ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀਆਂ ਕਾਰਵਾਈਆਂ ਰਿਕਾਰਡ ਹੁੰਦੀਆਂ ਹਨ। ਐਪਾਂ ਸਮੱਸਿਆਵਾਂ ਨੂੰ ਲੱਭਣ ਅਤੇ ਉਨ੍ਹਾਂ ਦਾ ਹੱਲ ਕਰਨ ਲਈ ਇਨ੍ਹਾਂ ਲੌਗਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੀਆਂ ਹਨ।\n\nਕੁਝ ਲੌਗਾਂ ਵਿੱਚ ਸੰਵੇਦਨਸ਼ੀਲ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੋ ਸਕਦੀ ਹੈ, ਇਸ ਲਈ ਸਿਰਫ਼ ਆਪਣੀਆਂ ਭਰੋਸੇਯੋਗ ਐਪਾਂ ਨੂੰ ਹੀ ਸਾਰੇ ਡੀਵਾਈਸ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ। \n\nਜੇ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਡੀਵਾਈਸ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੰਦੇ ਹੋ, ਤਾਂ ਇਹ ਹਾਲੇ ਵੀ ਆਪਣੇ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ। ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਨਿਰਮਾਤਾ ਹਾਲੇ ਵੀ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਮੌਜੂਦ ਕੁਝ ਲੌਗਾਂ ਜਾਂ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦਾ ਹੈ।"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"ਦੁਬਾਰਾ ਨਾ ਦਿਖਾਓ"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ਦੀ <xliff:g id="APP_2">%2$s</xliff:g> ਦੇ ਹਿੱਸੇ ਦਿਖਾਉਣ ਦੀ ਇੱਛਾ ਹੈ"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ਸੰਪਾਦਨ ਕਰੋ"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ਕਿਰਿਆਸ਼ੀਲ ਐਪਾਂ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> ਤੋਂ ਫ਼ੋਨ ਦੇ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> ਤੋਂ ਟੈਬਲੈੱਟ ਦੇ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"ਸਟ੍ਰੀਮਿੰਗ ਦੌਰਾਨ ਇਸ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਸਦੀ ਬਜਾਏ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
<string name="system_locale_title" msgid="711882686834677268">"ਸਿਸਟਮ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
<string name="default_card_name" msgid="9198284935962911468">"ਕਾਰਡ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 89c9f31..ed1e709 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Odczyt odcisku palca został anulowany."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Odczyt odcisku palca został anulowany przez użytkownika."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Zbyt wiele prób. Spróbuj ponownie później."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Zbyt wiele prób. Czytnik linii papilarnych został wyłączony."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Spróbuj ponownie."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nie zarejestrowano odcisków palców."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"nie wybrano"</string>
<string name="selected" msgid="6614607926197755875">"wybrano"</string>
<string name="not_selected" msgid="410652016565864475">"nie wybrano"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 gwiazdka na {max}}few{# gwiazdki na {max}}many{# gwiazdek na {max}}other{# gwiazdki na {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"w toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Wykonaj czynność przez..."</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Wykonaj czynność w aplikacji %1$s"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Ustawienie regionu"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Wpisz nazwę języka"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugerowane"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Sugerowane"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Sugerowane języki"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Sugerowane regiony"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Wszystkie języki"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Zezwolić aplikacji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> na dostęp do wszystkich dzienników urządzenia?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Zezwól na jednorazowy dostęp"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nie zezwalaj"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Dzienniki urządzenia zapisują, co dzieje się na urządzeniu. Aplikacje mogą ich używać do wykrywania i rozwiązywania problemów.\n\nNiektóre dzienniki mogą zawierać poufne dane, dlatego na dostęp do wszystkich dzienników zezwalaj tylko aplikacjom, którym ufasz. \n\nNawet jeśli nie zezwolisz tej aplikacji na dostęp do wszystkich dzienników na urządzeniu, będzie mogła korzystać z własnych. Producent urządzenia nadal będzie mógł używać niektórych dzienników na urządzeniu."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Nie pokazuj ponownie"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Aplikacja <xliff:g id="APP_0">%1$s</xliff:g> chce pokazywać wycinki z aplikacji <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Edytuj"</string>
@@ -2295,8 +2293,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Sprawdź aktywne aplikacje"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nie można korzystać z aparatu telefonu na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nie można korzystać z aparatu tabletu na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Nie można z tego skorzystać podczas strumieniowania. Użyj telefonu."</string>
<string name="system_locale_title" msgid="711882686834677268">"Ustawienie domyślne systemu"</string>
<string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d841ece..ce63b94 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Excesso de tentativas. Tente novamente mais tarde."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Excesso de tentativas. Sensor de impressão digital desativado."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 de {max} estrelas}one{# de {max} estrelas}other{# de {max} estrelas}}"</string>
<string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferência de região"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Digitar nome do idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugeridos"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Sugestões"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Idiomas sugeridos"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regiões sugeridas"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Todos os idiomas"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Permitir que o app <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acesse todos os registros do dispositivo?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir o acesso único"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Não permitir"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Os registros do dispositivo gravam o que acontece nele. Os apps podem usar esses registros para encontrar e corrigir problemas.\n\nAlguns registros podem conter informações sensíveis, então autorize o acesso a eles apenas para os apps em que você confia. \n\nSe você não permitir que esse app acesse todos os registros do dispositivo, ele ainda vai poder acessar os próprios. O fabricante do dispositivo também pode ter acesso a alguns registros ou informações."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Não mostrar novamente"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quer mostrar partes do app <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 08519d8..55cb68b 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo utilizador."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiadas tentativas. Tente novamente mais tarde."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Demasiadas tentativas. Sensor de impressões digitais desativado."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"não selecionado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Uma estrela de {max}}other{# estrelas de {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"em curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Concluir ação utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir ação utilizando %1$s"</string>
@@ -1426,7 +1426,7 @@
<string name="ext_media_browse_action" msgid="344865351947079139">"Explorar"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Saída do interruptor"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> em falta"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"Volte a inserir o dispositivo."</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"Volte a inserir o dispositivo"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"A mover <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"A mover dados"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"Transf. de conteúdo concluída"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferência de região"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Intr. nome do idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugeridos"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Sugeridas"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Idiomas sugeridos"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regiões sugeridas"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Todos os idiomas"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Permitir que a app <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> aceda a todos os registos do dispositivo?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir acesso único"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Não permitir"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Os registos do dispositivo documentam o que ocorre no seu dispositivo. As apps podem usar esses registos para detetar e corrigir problemas.\n\nAlguns registos podem conter informações confidenciais e, por isso, o acesso a todos os registos do dispositivo deve apenas ser permitido às apps nas quais confia. \n\nSe não permitir o acesso desta app a todos os registos do dispositivo, esta pode ainda assim aceder aos próprios registos. O fabricante do dispositivo pode continuar a aceder a alguns registos ou informações no seu dispositivo."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Não mostrar de novo"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"A app <xliff:g id="APP_0">%1$s</xliff:g> pretende mostrar partes da app <xliff:g id="APP_2">%2$s</xliff:g>."</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d841ece..ce63b94 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Excesso de tentativas. Tente novamente mais tarde."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Excesso de tentativas. Sensor de impressão digital desativado."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 de {max} estrelas}one{# de {max} estrelas}other{# de {max} estrelas}}"</string>
<string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferência de região"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Digitar nome do idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugeridos"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Sugestões"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Idiomas sugeridos"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regiões sugeridas"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Todos os idiomas"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Permitir que o app <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acesse todos os registros do dispositivo?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir o acesso único"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Não permitir"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Os registros do dispositivo gravam o que acontece nele. Os apps podem usar esses registros para encontrar e corrigir problemas.\n\nAlguns registros podem conter informações sensíveis, então autorize o acesso a eles apenas para os apps em que você confia. \n\nSe você não permitir que esse app acesse todos os registros do dispositivo, ele ainda vai poder acessar os próprios. O fabricante do dispositivo também pode ter acesso a alguns registros ou informações."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Não mostrar novamente"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quer mostrar partes do app <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 88c1af8..1e050e1 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -606,7 +606,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operațiunea privind amprenta a fost anulată."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operațiunea privind amprenta a fost anulată de utilizator."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Prea multe încercări. Încercați din nou mai târziu."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Prea multe încercări. Senzorul de amprentă este dezactivat."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Încercați din nou."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string>
@@ -965,7 +966,7 @@
<string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Pentru a debloca, conectați-vă folosind Contul Google."</string>
<string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Nume de utilizator (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Parolă"</string>
- <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Conectați-vă"</string>
+ <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Conectează-te"</string>
<string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"Nume de utilizator sau parolă nevalide."</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"Ați uitat numele de utilizator sau parola?\nAccesați "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"Se verifică..."</string>
@@ -1168,8 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"nebifat"</string>
<string name="selected" msgid="6614607926197755875">"selectat"</string>
<string name="not_selected" msgid="410652016565864475">"neselectat"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{O stea din {max}}few{# stele din {max}}other{# de stele din {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"în curs"</string>
<string name="whichApplication" msgid="5432266899591255759">"Finalizare acțiune utilizând"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Finalizați acțiunea utilizând %1$s"</string>
@@ -1655,7 +1655,7 @@
<string name="kg_login_instructions" msgid="3619844310339066827">"Pentru a debloca, conectați-vă cu Contul dvs. Google."</string>
<string name="kg_login_username_hint" msgid="1765453775467133251">"Nume de utilizator (e-mail)"</string>
<string name="kg_login_password_hint" msgid="3330530727273164402">"Parolă"</string>
- <string name="kg_login_submit_button" msgid="893611277617096870">"Conectați-vă"</string>
+ <string name="kg_login_submit_button" msgid="893611277617096870">"Conectează-te"</string>
<string name="kg_login_invalid_input" msgid="8292367491901220210">"Nume de utilizator sau parolă nevalide."</string>
<string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Ați uitat numele de utilizator sau parola?\nAccesați "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="4676010303243317253">"Se verifică contul…"</string>
@@ -1926,8 +1926,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Regiunea preferată"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Numele limbii"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugerate"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Sugerate"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Limbi sugerate"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regiuni sugerate"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Toate limbile"</string>
@@ -2052,8 +2051,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Permiteți ca <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> să acceseze toate jurnalele dispozitivului?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permiteți accesul o dată"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nu permiteți"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Jurnalele dispozitivului înregistrează activitatea de pe dispozitivul tău. Aplicațiile pot folosi aceste jurnale pentru a identifica și a remedia probleme.\n\nUnele jurnale pot să conțină informații sensibile, prin urmare permite accesul la toate jurnalele dispozitivului doar aplicațiilor în care ai încredere. \n\nDacă nu permiți accesul aplicației la toate jurnalele dispozitivului, aceasta poate în continuare să acceseze propriile jurnale. Este posibil ca producătorul dispozitivului să acceseze în continuare unele jurnale sau informații de pe dispozitiv."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Nu mai afișa"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vrea să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Editați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 98bac2f..0f21c90 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операция с отпечатком отменена."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операция с отпечатком пальца отменена пользователем."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Слишком много попыток. Повторите позже."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Слишком много попыток. Сканер отпечатков пальцев отключен."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Повторите попытку."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Нет отсканированных отпечатков пальцев"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"не отмечено"</string>
<string name="selected" msgid="6614607926197755875">"выбрано"</string>
<string name="not_selected" msgid="410652016565864475">"не выбрано"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Одна звезда из {max}}one{# звезда из {max}}few{# звезды из {max}}many{# звезд из {max}}other{# звезды из {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"в процессе"</string>
<string name="whichApplication" msgid="5432266899591255759">"Что использовать?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Выполнить с помощью приложения \"%1$s\""</string>
@@ -1427,7 +1427,7 @@
<string name="ext_media_unmount_action" msgid="966992232088442745">"Извлечь"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"Обзор"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Сменить устройство вывода"</string>
- <string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> не найден"</string>
+ <string name="ext_media_missing_title" msgid="3209472091220515046">"Устройство \"<xliff:g id="NAME">%s</xliff:g>\" не найдено"</string>
<string name="ext_media_missing_message" msgid="4408988706227922909">"Подключите накопитель снова."</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"Перенос приложения <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Перенос данных"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Региональные настройки"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Введите название языка"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Рекомендуемые"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Доступные регионы"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Рекомендуемые языки"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Предлагаемые регионы"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Все языки"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Разрешить приложению \"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>\" доступ ко всем журналам устройства?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Разрешить разовый доступ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Запретить"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"В журналы записывается информация о том, что происходит на устройстве. Приложения могут использовать их, чтобы находить и устранять неполадки.\n\nТак как некоторые журналы могут содержать конфиденциальную информацию, доступ ко всем журналам следует предоставлять только тем приложениям, которым вы доверяете. \n\nЕсли вы не предоставите такой доступ этому приложению, оно по-прежнему сможет просматривать свои журналы. Не исключено, что некоторые журналы или сведения на вашем устройстве будут по-прежнему доступны его производителю."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Больше не показывать"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Приложение \"<xliff:g id="APP_0">%1$s</xliff:g>\" запрашивает разрешение на показ фрагментов приложения \"<xliff:g id="APP_2">%2$s</xliff:g>\"."</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Изменить"</string>
@@ -2295,8 +2293,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Проверить активные приложения"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"У устройства \"<xliff:g id="DEVICE">%1$s</xliff:g>\" нет доступа к камере телефона."</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"У устройства \"<xliff:g id="DEVICE">%1$s</xliff:g>\" нет доступа к камере планшета."</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Этот контент недоступен во время трансляции. Используйте телефон."</string>
<string name="system_locale_title" msgid="711882686834677268">"Системные настройки по умолчанию"</string>
<string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index a2979e4..e96e0e4 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ඇඟිලි සලකුණු මෙහෙයුම අවලංගු කරන ලදී."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"පරිශීලක විසින් ඇඟිලි සලකුණු මෙහෙයුම අවසන් කරන ලදී."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"උත්සාහයන් ඉතා වැඩි ගණනකි. කරුණාකර පසුව නැවත උත්සාහ කරන්න."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"උත්සාහයන් ඉතා වැඩි ගණනකි. ඇඟිලි සලකුණු සංවේදකය අබල කරන ලදී."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"පරීක්ෂා කර නැත"</string>
<string name="selected" msgid="6614607926197755875">"තෝරන ලදි"</string>
<string name="not_selected" msgid="410652016565864475">"තෝරා නොමැත"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max}න් එක තරුවක්}one{{max}න් තරු #ක්}other{{max}න් තරු #ක්}}"</string>
<string name="in_progress" msgid="2149208189184319441">"සිදු වෙමින් පවතී"</string>
<string name="whichApplication" msgid="5432266899591255759">"පහත භාවිතයෙන් ක්රියාව සම්පූර්ණ කරන්න"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s භාවිතා කරමින් ක්රියාව සම්පුර්ණ කරන්න"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ප්රදේශ මනාපය"</string>
<string name="search_language_hint" msgid="7004225294308793583">"භාෂා නම ටයිප් කරන්න"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"යෝජිත"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"යෝජිත"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"යෝජිත භාෂා"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"යෝජිත කලාප"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"සියලු භාෂා"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> හට සියලු උපාංග ලොග ප්රවේශ වීමට ඉඩ දෙන්නද?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"එක් වරක් ප්රවේශය ඉඩ දෙන්න"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ඉඩ නොදෙන්න"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"උපාංග ලොග ඔබේ උපාංගයෙහි සිදු වන දේ වාර්තා කරයි. ගැටලු සොයා ගැනීමට සහ නිරාකරණයට යෙදුම්වලට මෙම ලොග භාවිතා කළ හැක.\n\nසමහර ලොගවල සංවේදී තතු අඩංගු විය හැකි බැවින්, ඔබ විශ්වාස කරන යෙදුම්වලට පමණක් සියලු උපාංග ලොග වෙත ප්රවේශ වීමට ඉඩ දෙන්න. \n\nඔබ මෙම යෙදුමට සියලු උපාංග ලොග වෙත ප්රවේශ වීමට ඉඩ නොදෙන්නේ නම්, එයට තවමත් එහිම ලොග වෙත ප්රවේශ විය හැක. ඔබේ උපාංග නිෂ්පාදකයාට තවමත් ඔබේ උපාංගයෙහි සමහර ලොග හෝ තතු වෙත ප්රවේශ විය හැක."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"නැවත නොපෙන්වන්න"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> හට කොටස් <xliff:g id="APP_2">%2$s</xliff:g>ක් පෙන්වීමට අවශ්යයි"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"සංස්කරණය"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"සක්රිය යෙදුම් පරීක්ෂා කරන්න"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ඔබගේ <xliff:g id="DEVICE">%1$s</xliff:g> වෙතින් දුරකථනයේ කැමරාවට ප්රවේශ විය නොහැකිය"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ඔබගේ <xliff:g id="DEVICE">%1$s</xliff:g> වෙතින් ටැබ්ලටයේ කැමරාවට ප්රවේශ විය නොහැකිය"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"ප්රවාහය කරන අතරේ මෙයට ප්රවේශ විය නොහැක. ඒ වෙනුවට ඔබේ දුරකථනයෙහි උත්සාහ කරන්න."</string>
<string name="system_locale_title" msgid="711882686834677268">"පද්ධති පෙරනිමිය"</string>
<string name="default_card_name" msgid="9198284935962911468">"කාඩ්පත <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index c23f839..566f448 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Overenie odtlačku prsta zrušil používateľ."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Príliš veľa pokusov. Skúste to neskôr."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Príliš veľa pokusov. Senzor odtlačkov prstov bol deaktivovaný."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Skúste to znova"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"nezačiarknuté"</string>
<string name="selected" msgid="6614607926197755875">"vybrané"</string>
<string name="not_selected" msgid="410652016565864475">"nevybrané"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 hviezdička z {max}}few{# hviezdičky z {max}}many{# hviezdičky z {max}}other{# hviezdičiek z {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"prebieha"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončiť akciu pomocou aplikácie"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončiť akciu pomocou aplikácie %1$s"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferovaný región"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Zadajte názov jazyka"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Navrhované"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Navrhované"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Navrhované jazyky"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Navrhované oblasti"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Všetky jazyky"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Chcete povoliť aplikácii <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> prístup k všetkým denníkom zariadenia?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Povoliť jednorazový prístup"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nepovoliť"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Denníky zariadenia zaznamenávajú, čo sa deje vo vašom zariadení. Aplikácie môžu pomocou týchto denníkov vyhľadávať a riešiť problémy.\n\nNiektoré denníky môžu obsahovať citlivé údaje, preto povoľte prístup k všetkým denníkom zariadenia iba dôveryhodným aplikáciám. \n\nAk tejto aplikácii nepovolíte prístup k všetkým denníkom zariadenia, stále bude mať prístup k vlastným denníkom. Výrobca vášho zariadenia bude mať naďalej prístup k niektorým denníkom alebo informáciám vo vašom zariadení."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Už nezobrazovať"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> chce zobrazovať rezy z aplikácie <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Upraviť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 99986c3..af74eba 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Dejanje s prstnim odtisom je bilo preklicano."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Dejanje s prstnim odtisom je preklical uporabnik."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Preveč poskusov. Poskusite znova pozneje."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Preveč poskusov. Tipalo prstnih odtisov je onemogočeno."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Poskusite znova."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ni registriranih prstnih odtisov."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"ni potrjeno"</string>
<string name="selected" msgid="6614607926197755875">"izbrano"</string>
<string name="not_selected" msgid="410652016565864475">"ni izbrano"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Ena zvezdica od {max}}one{# zvezdica od {max}}two{# zvezdici od {max}}few{# zvezdice od {max}}other{# zvezdic od {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"v teku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z aplikacijo"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončanje dejanja z aplikacijo %1$s"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Nastavitev območja"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Vnesite ime jezika"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Predlagano"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Predlagano"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Predlagani jeziki"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Predlagana območja"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Vsi jeziki"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Ali aplikaciji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> dovolite dostop do vseh dnevnikov naprave?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Dovoli enkratni dostop"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne dovoli"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"V dnevnikih naprave se beleži dogajanje v napravi. Aplikacije lahko te dnevnike uporabijo za iskanje in odpravljanje težav.\n\nNekateri dnevniki morda vsebujejo občutljive podatke, zato dostop do vseh dnevnikov naprave omogočite le aplikacijam, ki jim zaupate. \n\nČe tej aplikaciji ne dovolite dostopa do vseh dnevnikov naprave, bo aplikacija kljub temu lahko dostopala do svojih dnevnikov. Proizvajalec naprave bo morda lahko kljub temu dostopal do nekaterih dnevnikov ali podatkov v napravi."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikaži več"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi prikazati izreze aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Uredi"</string>
@@ -2295,8 +2293,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Preverite aktivne aplikacije"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ni mogoče dostopati do fotoaparata telefona prek naprave <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ni mogoče dostopati do fotoaparata tabličnega računalnika prek naprave <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Do te vsebine ni mogoče dostopati med pretočnim predvajanjem. Poskusite s telefonom."</string>
<string name="system_locale_title" msgid="711882686834677268">"Sistemsko privzeto"</string>
<string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 93ae3e8..c359aad 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operacioni i gjurmës së gishtit u anulua."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Veprimi i gjurmës së gishtit u anulua nga përdoruesi."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Keni bërë shumë tentativa. Provo përsëri më vonë."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Shumë përpjekje. Sensori i gjurmës së gishtit u çaktivizua."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Provo përsëri."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nuk ka asnjë gjurmë gishti të regjistruar."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"nuk u përzgjodh"</string>
<string name="selected" msgid="6614607926197755875">"i zgjedhur"</string>
<string name="not_selected" msgid="410652016565864475">"i pazgjedhur"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Një yll nga {max}}other{# yje nga {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"në vazhdim"</string>
<string name="whichApplication" msgid="5432266899591255759">"Përfundo veprimin duke përdorur"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Përfundo veprimin duke përdorur %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Preferenca e rajonit"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Shkruaj emrin e gjuhës"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugjeruar"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Të sugjeruara"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Gjuhët e sugjeruara"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Rajonet e sugjeruara"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Të gjitha gjuhët"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Të lejohet që <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> të ketë qasje te të gjitha evidencat e pajisjes?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Lejo qasjen vetëm për një herë"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Mos lejo"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Evidencat e pajisjes regjistrojnë çfarë ndodh në pajisjen tënde. Aplikacionet mund t\'i përdorin këto evidenca për të gjetur dhe rregulluar problemet.\n\nDisa evidenca mund të përmbajnë informacione delikate, ndaj lejo vetëm aplikacionet që u beson të kenë qasje te të gjitha evidencat e pajisjes. \n\nNëse nuk e lejon këtë aplikacion që të ketë qasje te të gjitha evidencat e pajisjes, ai mund të vazhdojë të ketë qasje tek evidencat e tij. Prodhuesi i pajisjes sate mund të jetë ende në gjendje që të ketë qasje te disa evidenca ose informacione në pajisjen tënde."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Mos e shfaq më"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> dëshiron të shfaqë pjesë të <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Modifiko"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Kontrollo aplikacionet aktive"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nuk mund të qasesh në kamerën e telefonit tënd nga <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nuk mund të qasesh në kamerën e tabletit tënd nga <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Nuk mund të kesh qasje në të gjatë transmetimit. Provoje në telefon më mirë."</string>
<string name="system_locale_title" msgid="711882686834677268">"Parazgjedhja e sistemit"</string>
<string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6f0fc17..6d29046 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -606,7 +606,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Радња са отиском прста је отказана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисник је отказао радњу са отиском прста."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Превише покушаја. Пробајте поново касније."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Превише покушаја. Сензор за отисак прста је онемогућен."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Пробајте поново."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Није регистрован ниједан отисак прста."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string>
@@ -1168,8 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"није означено"</string>
<string name="selected" msgid="6614607926197755875">"изабрано"</string>
<string name="not_selected" msgid="410652016565864475">"није изабрано"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Једна звездица од {max}}one{# звездица од {max}}few{# звездице од {max}}other{# звездица од {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"у току"</string>
<string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
@@ -1926,8 +1926,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Подешавање региона"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Унесите назив језика"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Предложени"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Предложено"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Предложени језици"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Предложени региони"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Сви језици"</string>
@@ -2052,8 +2051,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Желите да дозволите апликацији <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> да приступа свим евиденцијама уређаја?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Дозволи једнократан приступ"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дозволи"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Евиденције уређаја региструју шта се дешава на уређају. Апликације могу да користе те евиденције да би пронашле и решиле проблеме.\n\nНеке евиденције могу да садрже осетљиве информације, па приступ свим евиденцијама уређаја треба да дозвољавате само апликацијама у које имате поверења. \n\nАко не дозволите овој апликацији да приступа свим евиденцијама уређаја, она и даље може да приступа сопственим евиденцијама. Произвођач уређаја ће можда и даље моћи да приступа неким евиденцијама или информацијама на уређају."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Не приказуј поново"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Апликација <xliff:g id="APP_0">%1$s</xliff:g> жели да приказује исечке из апликације <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Измени"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index a5515b1..1ee3192 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -237,7 +237,7 @@
<string name="global_actions" product="default" msgid="6410072189971495460">"Telefonalternativ"</string>
<string name="global_action_lock" msgid="6949357274257655383">"Skärmlås"</string>
<string name="global_action_power_off" msgid="4404936470711393203">"Stäng av"</string>
- <string name="global_action_power_options" msgid="1185286119330160073">"Strömbrytare"</string>
+ <string name="global_action_power_options" msgid="1185286119330160073">"Av/på"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Starta om"</string>
<string name="global_action_emergency" msgid="1387617624177105088">"Nödsituation"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"Felrapport"</string>
@@ -605,13 +605,14 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrycksåtgärden avbröts."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrycksåtgärden avbröts av användaren."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Du har gjort för många försök. Försök igen senare."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Du har försökt för många gånger. Fingeravtryckssensorn har inaktiverats."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Försök igen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string>
<string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Det går inte att använda fingeravtryckssensorn. Besök ett reparationsställe"</string>
- <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Strömbrytaren nedtryckt"</string>
+ <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Av/på-knappen nedtryckt"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Använd ditt fingeravtryck"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Använd ditt fingeravtryck eller skärmlåset"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"inte markerad"</string>
<string name="selected" msgid="6614607926197755875">"valt"</string>
<string name="not_selected" msgid="410652016565864475">"inte valt"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{En stjärna av {max}}other{# stjärnor av {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"pågår"</string>
<string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd genom att använda"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Slutför åtgärden med %1$s"</string>
@@ -1248,11 +1248,11 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> förbereds."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Appar startas."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Uppgraderingen är klar."</string>
- <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du tryckte på strömbrytaren, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt när du konfigurerar fingeravtrycket."</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du tryckte på av/på-knappen, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt när du konfigurerar fingeravtrycket."</string>
<string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tryck för att stänga av skärmen"</string>
<string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Stäng av skärmen"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vill du verifiera ditt fingeravtryck?"</string>
- <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du tryckte på strömbrytaren, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt för att verifiera ditt fingeravtryck."</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du tryckte på av/på-knappen, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt för att verifiera ditt fingeravtryck."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Stäng av skärmen"</string>
<string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortsätt"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> körs"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Regionsinställningar"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Ange språk"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Förslag"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Förslag"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Föreslagna språk"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Föreslagna regioner"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Alla språk"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Vill du tillåta att <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> får åtkomst till alla enhetsloggar?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Tillåt engångsåtkomst"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Tillåt inte"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"I enhetsloggar registreras vad som händer på enheten. Appar kan använda dessa loggar för att hitta och åtgärda problem.\n\nVissa loggar kan innehålla känsliga uppgifter, så du ska bara bevilja appar du litar på åtkomst till alla enhetsloggar. \n\nEn app har åtkomst till sina egna loggar även om du inte ger den åtkomst till alla enhetsloggar. Enhetens tillverkare kan fortfarande ha åtkomst till vissa loggar eller viss information på enheten."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Visa inte igen"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vill kunna visa bitar av <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Redigera"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Kontrollera aktiva appar"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Telefonens kamera kan inte användas från <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Surfplattans kamera kan inte användas från <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Det går inte att komma åt innehållet när du streamar. Testa med telefonen i stället."</string>
<string name="system_locale_title" msgid="711882686834677268">"Systemets standardinställning"</string>
<string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 0552c2c..8659b6a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Mchakato wa alama ya kidole umeghairiwa."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Mtumiaji ameghairi uthibitishaji wa alama ya kidole."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Majaribio mengi mno. Jaribu tena baadaye."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Majaribio mengi mno. Kitambua alama ya kidole kimezimwa."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Jaribu tena."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hakuna alama za vidole zilizojumuishwa."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"haijateuliwa"</string>
<string name="selected" msgid="6614607926197755875">"imechaguliwa"</string>
<string name="not_selected" msgid="410652016565864475">"haijachaguliwa"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Nyota moja kati ya {max}}other{Nyota # kati ya {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"inaendelea"</string>
<string name="whichApplication" msgid="5432266899591255759">"Kamilisha kitendo ukitumia"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Kamilisha kitendo ukitumia %1$s"</string>
@@ -1566,7 +1566,7 @@
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="8490227947584914460">"Hifadhi ya ndani ya pamoja"</string>
<string name="storage_sd_card" msgid="3404740277075331881">"Kadi ya SD"</string>
- <string name="storage_sd_card_label" msgid="7526153141147470509">"Kadi ya SD iliyotengenezwa na <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
+ <string name="storage_sd_card_label" msgid="7526153141147470509">"Kadi ya SD ya <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"Hifadhi ya USB"</string>
<string name="storage_usb_drive_label" msgid="6631740655876540521">"Hifadhi ya USB iliyotengenezwa na <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb" msgid="2391213347883616886">"Hifadhi ya USB"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Mapendeleo ya eneo"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Weka jina la lugha"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Zinazopendekezwa"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Yanayopendekezwa"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Lugha zinazopendekezwa"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Maeneo yanayopendekezwa"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Lugha zote"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Ungependa kuruhusu <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ifikie kumbukumbu zote za kifaa?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Ruhusu ufikiaji wa mara moja"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Usiruhusu"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Kumbukumbu za kifaa zinarekodi kinachofanyika kwenye kifaa chako. Programu zinaweza kutumia kumbukumbu hizi ili kutambua na kurekebisha hitilafu.\n\nBaadhi ya kumbukumbu huenda zikawa na taarifa nyeti, hivyo ruhusu tu programu unazoziamini kufikia kumbukumbu zote za kifaa. \n\nIwapo hutaruhusu programu hii ifikie kumbukumbu zote za kifaa, bado inaweza kufikia kumbukumbu zake yenyewe. Huenda mtengenezaji wa kifaa chako bado akaweza kufikia baadhi ya kumbukumbu au taarifa zilizopo kwenye kifaa chako."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Usionyeshe tena"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> inataka kuonyesha vipengee <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Badilisha"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Angalia programu zinazotumika"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Haiwezi kufikia kamera ya simu kutoka kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Haiwezi kufikia kamera ya kompyuta kibao kutoka kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Huwezi kufikia maudhui haya unapotiririsha. Badala yake jaribu kwenye simu yako."</string>
<string name="system_locale_title" msgid="711882686834677268">"Chaguomsingi la mfumo"</string>
<string name="default_card_name" msgid="9198284935962911468">"SIM KADI <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 21c457b..611eadf 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"கைரேகை செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"பயனர், கைரேகை உறுதிப்படுத்துதலை ரத்துசெய்தார்."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"அதிகமான முயற்சிகள். பிறகு முயற்சிக்கவும்."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"பலமுறை முயன்றுவிட்டீர்கள். கைரேகை சென்சார் முடக்கப்பட்டது."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"மீண்டும் முயற்சிக்கவும்."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"கைரேகைப் பதிவுகள் எதுவும் இல்லை."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"முடக்கப்பட்டுள்ளது"</string>
<string name="selected" msgid="6614607926197755875">"தேர்ந்தெடுக்கப்பட்டது"</string>
<string name="not_selected" msgid="410652016565864475">"தேர்ந்தெடுக்கப்படவில்லை"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{ஒரு நட்சத்திரம்/{max}}other{# நட்சத்திரங்கள்/{max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"செயலிலுள்ளது"</string>
<string name="whichApplication" msgid="5432266899591255759">"இதைப் பயன்படுத்தி செயலை நிறைவுசெய்"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ஐப் பயன்படுத்தி செயலை முடிக்கவும்"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"மண்டல விருப்பம்"</string>
<string name="search_language_hint" msgid="7004225294308793583">"மொழி பெயரை உள்ளிடுக"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"பரிந்துரைகள்"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"பரிந்துரைக்கப்படுபவை"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"பரிந்துரைக்கப்படும் மொழிகள்"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"பரிந்துரைக்கப்படும் பகுதிகள்"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"எல்லா மொழிகளும்"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"சாதனப் பதிவுகள் அனைத்தையும் <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> அணுக அனுமதிக்கவா?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"ஒருமுறை அணுகலை அனுமதி"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"அனுமதிக்க வேண்டாம்"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"உங்கள் சாதனத்தில் நடப்பவற்றைச் சாதனப் பதிவுகள் ரெக்கார்டு செய்யும். சிக்கல்களைக் கண்டறிந்து சரிசெய்ய ஆப்ஸ் இந்தப் பதிவுகளைப் பயன்படுத்தலாம்.\n\nபாதுகாக்கப்பட வேண்டிய தகவல்கள் சில பதிவுகளில் இருக்கக்கூடும் என்பதால் சாதனப் பதிவுகள் அனைத்தையும் அணுக நீங்கள் நம்பும் ஆப்ஸை மட்டும் அனுமதிக்கவும். \n\nசாதனப் பதிவுகள் அனைத்தையும் அணுக இந்த ஆப்ஸை அனுமதிக்கவில்லை என்றாலும் அதற்குச் சொந்தமான பதிவுகளை அதனால் அணுக முடியும். உங்கள் சாதனத்திலுள்ள சில பதிவுகளையோ தகவல்களையோ சாதன உற்பத்தியாளரால் தொடர்ந்து அணுக முடியும்."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"மீண்டும் காட்டாதே"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_2">%2$s</xliff:g> ஆப்ஸின் விழிப்பூட்டல்களைக் காண்பிக்க, <xliff:g id="APP_0">%1$s</xliff:g> அனுமதி கேட்கிறது"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"திருத்து"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 6ae3f21..a56a628 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర యాక్టివిటీ రద్దయింది."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేశారు."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"చాలా ఎక్కువ ప్రయత్నాలు చేశారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"అనేకసార్లు ప్రయత్నించారు. వేలిముద్ర సెన్సార్ నిలిపివేయబడింది."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ఎంచుకోలేదు"</string>
<string name="selected" msgid="6614607926197755875">"ఎంచుకోబడింది"</string>
<string name="not_selected" msgid="410652016565864475">"ఎంచుకోబడలేదు"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max}కి ఒక స్టార్}other{{max}కి # స్టార్లు}}"</string>
<string name="in_progress" msgid="2149208189184319441">"ప్రోగ్రెస్లో ఉంది"</string>
<string name="whichApplication" msgid="5432266899591255759">"దీన్ని ఉపయోగించి చర్యను పూర్తి చేయండి"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sను ఉపయోగించి చర్యను పూర్తి చేయి"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ప్రాంతం ప్రాధాన్యత"</string>
<string name="search_language_hint" msgid="7004225294308793583">"భాష పేరును టైప్ చేయండి"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"సూచించినవి"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"సూచించబడినవి"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"సూచించిన భాషలు"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"సూచించిన ప్రాంతాలు"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"అన్ని భాషలు"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"అన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>ను అనుమతించాలా?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"వన్-టైమ్ యాక్సెస్ను అనుమతించండి"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"అనుమతించవద్దు"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"మీ పరికరంలో జరిగే దాన్ని పరికర లాగ్లు రికార్డ్ చేస్తాయి. సమస్యలను కనుగొని, పరిష్కరించడానికి యాప్లు ఈ లాగ్లను ఉపయోగిస్తాయి.\n\nకొన్ని లాగ్లలో గోప్యమైన సమాచారం ఉండవచ్చు, కాబట్టి మీరు విశ్వసించే యాప్లను మాత్రమే అన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి అనుమతించండి. \n\nఅన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి మీరు ఈ యాప్ను అనుమతించకపోతే, అది తన స్వంత లాగ్లను ఇప్పటికి యాక్సెస్ చేయగలదు. మీ పరికర తయారీదారు ఇప్పటికీ మీ పరికరంలో కొన్ని లాగ్లు లేదా సమాచారాన్ని యాక్సెస్ చేయగలరు."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"మళ్లీ చూపవద్దు"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> స్లైస్లను చూపించాలనుకుంటోంది"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ఎడిట్ చేయండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index e490f70..a51876c 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ยกเลิกการทำงานของลายนิ้วมือ"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ผู้ใช้ยกเลิกการทำงานของลายนิ้วมือ"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ดำเนินการหลายครั้งเกินไป ลองอีกครั้งในภายหลัง"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"ลองหลายครั้งเกินไป ปิดใช้เซ็นเซอร์ลายนิ้วมือแล้ว"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ลองอีกครั้ง"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"ยังไม่เลือก"</string>
<string name="selected" msgid="6614607926197755875">"เลือกไว้"</string>
<string name="not_selected" msgid="410652016565864475">"ไม่ได้เลือกไว้"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 จาก {max} ดาว}other{# จาก {max} ดาว}}"</string>
<string name="in_progress" msgid="2149208189184319441">"กำลังดำเนินการ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ทำงานให้เสร็จโดยใช้"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ดำเนินการให้เสร็จสมบูรณ์โดยใช้ %1$s"</string>
@@ -1425,7 +1425,7 @@
<string name="ext_media_unmount_action" msgid="966992232088442745">"นำอุปกรณ์ออก"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"สำรวจ"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"เปลี่ยนเอาต์พุต"</string>
- <string name="ext_media_missing_title" msgid="3209472091220515046">"ไม่มี <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="ext_media_missing_title" msgid="3209472091220515046">"ไม่มี<xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_missing_message" msgid="4408988706227922909">"ใส่อุปกรณ์อีกครั้ง"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"กำลังย้าย <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"กำลังย้ายข้อมูล"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"ค่ากำหนดภูมิภาค"</string>
<string name="search_language_hint" msgid="7004225294308793583">"พิมพ์ชื่อภาษา"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"แนะนำ"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"แนะนำ"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"ภาษาที่แนะนำ"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"ภูมิภาคที่แนะนำ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ทุกภาษา"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"อนุญาตให้ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> เข้าถึงบันทึกทั้งหมดของอุปกรณ์ใช่ไหม"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"อนุญาตสิทธิ์เข้าถึงแบบครั้งเดียว"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"ไม่อนุญาต"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"บันทึกของอุปกรณ์เก็บข้อมูลสิ่งที่เกิดขึ้นในอุปกรณ์ แอปสามารถใช้บันทึกเหล่านี้เพื่อค้นหาและแก้ไขปัญหา\n\nบันทึกบางรายการอาจมีข้อมูลที่ละเอียดอ่อน คุณจึงควรอนุญาตเฉพาะแอปที่เชื่อถือได้ให้เข้าถึงบันทึกทั้งหมดของอุปกรณ์ \n\nหากคุณไม่อนุญาตให้แอปนี้เข้าถึงบันทึกทั้งหมดของอุปกรณ์ แอปจะยังเข้าถึงบันทึกของตัวเองได้อยู่ ผู้ผลิตอุปกรณ์อาจยังเข้าถึงบันทึกหรือข้อมูลบางรายการในอุปกรณ์ของคุณได้"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"ไม่ต้องแสดงอีก"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ต้องการแสดงส่วนต่างๆ ของ <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"แก้ไข"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index b78380c..faacade 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Nakansela ang operasyong ginagamitan ng fingerprint."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kinansela ng user ang operasyon sa fingerprint."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Napakaraming pagtatangka. Subukan ulit sa ibang pagkakataon."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Masyadong maraming beses sumubok. Na-disable ang sensor para sa fingerprint."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Subukang muli."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Walang naka-enroll na fingerprint."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"hindi nilagyan ng check"</string>
<string name="selected" msgid="6614607926197755875">"pinili"</string>
<string name="not_selected" msgid="410652016565864475">"hindi pinili"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Isang star sa {max}}one{# star sa {max}}other{# na star sa {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"isinasagawa"</string>
<string name="whichApplication" msgid="5432266899591255759">"Kumpletuhin ang pagkilos gamit ang"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Tapusin ang pagkilos gamit ang %1$s"</string>
@@ -1426,7 +1426,7 @@
<string name="ext_media_browse_action" msgid="344865351947079139">"I-explore"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Ilipat ang output"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"Nawawala ang <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"Ikabit muli ang device"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"Ikabit ulit ang device"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"Inililipat ang <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Naglilipat ng data"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"Tapos na ang paglipat ng content"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Kagustuhan sa rehiyon"</string>
<string name="search_language_hint" msgid="7004225294308793583">"I-type ang wika"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Iminumungkahi"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Iminumungkahi"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Mga iminumungkahing wika"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Mga iminumungkahing rehiyon"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Lahat ng wika"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Payagan ang <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> na i-access ang lahat ng log ng device?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Payagan ang isang beses na pag-access"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Huwag payagan"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Nire-record ng mga log ng device kung ano ang nangyayari sa iyong device. Magagamit ng mga app ang mga log na ito para maghanap at mag-ayos ng mga isyu.\n\nPosibleng maglaman ang ilang log ng sensitibong impormasyon, kaya ang mga app lang na pinagkakatiwalaan mo ang payagang maka-access sa lahat ng log ng device. \n\nKung hindi mo papayagan ang app na ito na i-access ang lahat ng log ng device, maa-access pa rin nito ang mga sarili nitong log. Posible pa ring ma-access ng manufacturer ng iyong device ang ilang log o impormasyon sa device mo."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Huwag ipakita ulit"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"Gustong ipakita ng <xliff:g id="APP_0">%1$s</xliff:g> ang mga slice ng <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"I-edit"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 7c4c6e4..af503ca 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Parmak izi işlemi iptal edildi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Parmak izi işlemi kullanıcı tarafından iptal edildi."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Çok fazla deneme yapıldı. Parmak izi sensörü devre dışı bırakıldı."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tekrar deneyin."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Parmak izi kaydedilmedi."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"işaretli değil"</string>
<string name="selected" msgid="6614607926197755875">"seçili"</string>
<string name="not_selected" msgid="410652016565864475">"seçili değil"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} üzerinden bir yıldız}other{{max} üzerinden # yıldız}}"</string>
<string name="in_progress" msgid="2149208189184319441">"devam ediyor"</string>
<string name="whichApplication" msgid="5432266899591255759">"İşlemi şunu kullanarak tamamla"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"İşlemi %1$s kullanarak tamamla"</string>
@@ -1594,7 +1594,7 @@
<string name="validity_period" msgid="1717724283033175968">"Geçerlilik:"</string>
<string name="issued_on" msgid="5855489688152497307">"Yayınlanma tarihi:"</string>
<string name="expires_on" msgid="1623640879705103121">"Sona erme tarihi:"</string>
- <string name="serial_number" msgid="3479576915806623429">"Seri numara:"</string>
+ <string name="serial_number" msgid="3479576915806623429">"Seri numarası:"</string>
<string name="fingerprints" msgid="148690767172613723">"Parmak izleri:"</string>
<string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256 parmak izi:"</string>
<string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1 parmak izi:"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Bölge tercihi"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Dil adını yazın"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Önerilen"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Önerilen"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Önerilen diller"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Önerilen bölgeler"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Tüm diller"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> uygulamasının tüm cihaz günlüklerine erişmesine izin verilsin mi?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Tek seferlik erişim izni ver"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"İzin verme"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Cihaz günlükleri, cihazınızda olanları kaydeder. Uygulamalar, sorunları bulup düzeltmek için bu günlükleri kullanabilir.\n\nBazı günlükler hassas bilgiler içerebileceği için yalnızca güvendiğiniz uygulamaların tüm cihaz günlüklerine erişmesine izin verin. \n\nBu uygulamanın tüm cihaz günlüklerine erişmesine izin vermeseniz de kendi günlüklerine erişmeye devam edebilir. Ayrıca, cihaz üreticiniz de cihazınızdaki bazı günlüklere veya bilgilere erişmeye devam edebilir."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Bir daha gösterme"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> uygulaması, <xliff:g id="APP_2">%2$s</xliff:g> dilimlerini göstermek istiyor"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Düzenle"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Etkin uygulamaları kontrol edin"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan telefonun kamerasına erişilemiyor"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan tabletin kamerasına erişilemiyor"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Canlı oynatılırken bu içeriğe erişilemez. Bunun yerine telefonunuzu kullanmayı deneyin."</string>
<string name="system_locale_title" msgid="711882686834677268">"Sistem varsayılanı"</string>
<string name="default_card_name" msgid="9198284935962911468">"KART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 747e9a8..bac234a 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -607,7 +607,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Дію з відбитком пальця скасовано."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Користувач скасував дію з відбитком пальця."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Забагато спроб. Спробуйте пізніше."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Забагато спроб. Сканер відбитків пальців вимкнено."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Повторіть спробу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Відбитки пальців не зареєстровано."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string>
@@ -1169,8 +1170,7 @@
<string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
<string name="selected" msgid="6614607926197755875">"вибрано"</string>
<string name="not_selected" msgid="410652016565864475">"не вибрано"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Одна зірочка з {max}}one{# зірочка з {max}}few{# зірочки з {max}}many{# зірочок із {max}}other{# зірочки з {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"триває"</string>
<string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
@@ -1927,8 +1927,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Вибір регіону"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Введіть назву мови"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Рекомендовані"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Пропоновані"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Пропоновані мови"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Пропоновані регіони"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Усі мови"</string>
@@ -2053,8 +2052,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Надати додатку <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> доступ до всіх журналів пристрою?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Надати доступ лише цього разу"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дозволяти"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"У журналах пристрою реєструється все, що відбувається на ньому. За допомогою цих журналів додатки можуть виявляти й усувати проблеми.\n\nДеякі журнали можуть містити конфіденційні дані, тому надавати доступ до всіх журналів пристрою слід лише надійним додаткам. \n\nЯкщо додаток не має доступу до всіх журналів пристрою, він усе одно може використовувати власні журнали. Виробник вашого пристрою все одно може використовувати деякі журнали чи інформацію на ньому."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Більше не показувати"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> хоче показати фрагменти додатка <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Редагувати"</string>
@@ -2295,8 +2293,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Перевірте активні додатки"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не вдається отримати доступ до камери телефона з пристрою <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не вдається отримати доступ до камери планшета з пристрою <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Цей контент недоступний під час потокового передавання. Спробуйте натомість скористатися телефоном."</string>
<string name="system_locale_title" msgid="711882686834677268">"Налаштування системи за умовчанням"</string>
<string name="default_card_name" msgid="9198284935962911468">"КАРТКА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 7e2b994..7db1619 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی۔"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی۔"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"کافی زیادہ کوششیں کی گئیں۔ بعد میں دوبارہ کوشش کریں۔"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"کافی زیادہ کوششیں۔ فنگر پرنٹ سینسر غیر فعال ہو گیا۔"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"کوئی فنگر پرنٹ مندرج شدہ نہیں ہے۔"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"چیک نہیں کیا گیا"</string>
<string name="selected" msgid="6614607926197755875">"منتخب کردہ"</string>
<string name="not_selected" msgid="410652016565864475">"غیر منتخب کردہ"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{{max} میں سے ایک ستارہ}other{{max} میں سے # ستارے}}"</string>
<string name="in_progress" msgid="2149208189184319441">"جاری ہے"</string>
<string name="whichApplication" msgid="5432266899591255759">"اس کا استعمال کرکے کارروائی مکمل کریں"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s کا استعمال کر کے کارروائی مکمل کریں"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"علاقہ کی ترجیح"</string>
<string name="search_language_hint" msgid="7004225294308793583">"زبان کا نام ٹائپ کریں"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"تجویز کردہ"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"تجویز کردہ"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"تجویز کردہ زبانیں"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"تجویز کردہ علاقے"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"سبھی زبانیں"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> کو آلے کے تمام لاگز تک رسائی کی اجازت دیں؟"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"یک وقتی رسائی کی اجازت دیں"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"اجازت نہ دیں"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"آپ کے آلے پر جو ہوتا ہے آلے کے لاگز اسے ریکارڈ کر لیتے ہیں۔ ایپس ان لاگز کا استعمال مسائل کو تلاش کرنے اور ان کو حل کرنے کے لیے کر سکتی ہیں۔\n\nکچھ لاگز میں حساس معلومات شامل ہو سکتی ہیں، اس لیے صرف اپنے بھروسے مند ایپس کو ہی آلے کے تمام لاگز تک رسائی کی اجازت دیں۔ \n\nاگر آپ اس ایپ کو آلے کے تمام لاگز تک رسائی کی اجازت نہیں دیتے ہیں تب بھی یہ اپنے لاگز تک رسائی حاصل کر سکتی ہے۔ آپ کے آلے کا مینوفیکچرر اب بھی آپ کے آلے پر کچھ لاگز یا معلومات تک رسائی حاصل کر سکتا ہے۔"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"دوبارہ نہ دکھائیں"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> کے سلائسز دکھانا چاہتی ہے"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"ترمیم کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index a14a27a..8f4cceb 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmoq izi tekshiruvi bekor qilindi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmoq izi amali foydalanuvchi tomonidan bekor qilindi"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Urinishlar soni ko‘payib ketdi. Keyinroq qayta urinib ko‘ring."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Urinishlar soni ko‘payib ketdi. Barmoq izi skaneri bloklandi."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Qayta urinib ko‘ring."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hech qanday barmoq izi qayd qilinmagan."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
<string name="selected" msgid="6614607926197755875">"tanlangan"</string>
<string name="not_selected" msgid="410652016565864475">"tanlanmagan"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Maksimal {max} ta yulduzdan 1 yulduz}other{Maksimal {max} ta yulduzdan # yulduz}}"</string>
<string name="in_progress" msgid="2149208189184319441">"bajarilmoqda"</string>
<string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
@@ -1423,10 +1423,10 @@
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Chiqarib olinmasin"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"Sozlash"</string>
<string name="ext_media_unmount_action" msgid="966992232088442745">"Chiqarish"</string>
- <string name="ext_media_browse_action" msgid="344865351947079139">"O‘rganish"</string>
+ <string name="ext_media_browse_action" msgid="344865351947079139">"Ochish"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Audio chiqishni almashtirish"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> topilmadi"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"Qurilmani yana ulang"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"Qurilmani qayta ulang"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"<xliff:g id="NAME">%s</xliff:g> ko‘chirib o‘tkazilmoqda"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Ma’lumotlar ko‘chirilmoqda"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"Kontent ko‘chirildi"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Hudud sozlamalari"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Til nomini kiriting"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Taklif etiladi"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Tavsiya etiladi"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Tavsiya etilgan tillar"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Tavsiya etilgan mintaqalar"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Barcha tillar"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ilovasining qurilmadagi barcha jurnallarga kirishiga ruxsat berilsinmi?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Bir matalik foydalanishga ruxsat berish"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Rad etish"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Qurilma jurnaliga qurilma bilan yuz bergan hodisalar qaydlari yoziladi. Ilovalar bu jurnal qaydlari yordamida muammolarni topishi va bartaraf qilishi mumkin.\n\nAyrim jurnal qaydlarida maxfiy axborotlar yozilishi mumkin, shu sababli qurilmadagi barcha jurnal qaydlariga ruxsatni faqat ishonchli ilovalarga bering. \n\nBu ilovaga qurilmadagi barcha jurnal qaydlariga ruxsat berilmasa ham, u oʻzining jurnalini ocha oladi. Qurilma ishlab chiqaruvchisi ham ayrim jurnallar yoki qurilma haqidagi axborotlarni ocha oladi."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Boshqa chiqmasin"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ilovasi <xliff:g id="APP_2">%2$s</xliff:g> ilovasidan fragmentlar ko‘rsatish uchun ruxsat so‘ramoqda"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Tahrirlash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 39a46ee..40eb56d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Thao tác dùng dấu vân tay bị hủy."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Người dùng đã hủy thao tác dùng dấu vân tay."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Quá nhiều lần thử. Hãy thử lại sau."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Quá nhiều lần thử. Cảm biến vân tay đã bị tắt."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Thử lại."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Chưa đăng ký vân tay."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
<string name="selected" msgid="6614607926197755875">"đã chọn"</string>
<string name="not_selected" msgid="410652016565864475">"chưa được chọn"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1/{max} sao}other{#/{max} sao}}"</string>
<string name="in_progress" msgid="2149208189184319441">"đang thực hiện"</string>
<string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
@@ -1422,7 +1422,7 @@
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"Đang ngắt kết nối <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Không tháo"</string>
<string name="ext_media_init_action" msgid="2312974060585056709">"Thiết lập"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"Tháo"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"Ngắt kết nối"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"Khám phá"</string>
<string name="ext_media_seamless_action" msgid="8837030226009268080">"Chuyển đổi đầu ra"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> bị thiếu"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Tùy chọn khu vực"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Nhập tên ngôn ngữ"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Đề xuất"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Ðề xuất"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Ngôn ngữ đề xuất"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Khu vực đề xuất"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Tất cả ngôn ngữ"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Cho phép <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> truy cập vào tất cả các nhật ký thiết bị?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Cho phép truy cập một lần"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Không cho phép"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Nhật ký thiết bị ghi lại những hoạt động diễn ra trên thiết bị. Các ứng dụng có thể dùng nhật ký này để tìm và khắc phục sự cố.\n\nMột số nhật ký có thể chứa thông tin nhạy cảm, vì vậy, bạn chỉ nên cấp quyền truy cập vào toàn bộ nhật ký thiết bị cho những ứng dụng mà mình tin cậy. \n\nNếu bạn không cho phép ứng dụng này truy cập vào toàn bộ nhật ký thiết bị, thì ứng dụng vẫn có thể truy cập vào nhật ký của chính nó. Nhà sản xuất thiết bị vẫn có thể truy cập vào một số nhật ký hoặc thông tin trên thiết bị của bạn."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Không hiện lại"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> muốn hiển thị các lát của <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Chỉnh sửa"</string>
@@ -2293,8 +2291,7 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Xem các ứng dụng đang hoạt động"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Không truy cập được vào máy ảnh trên điện thoại từ <xliff:g id="DEVICE">%1$s</xliff:g> của bạn"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Không truy cập được vào máy ảnh trên máy tính bảng từ <xliff:g id="DEVICE">%1$s</xliff:g> của bạn"</string>
- <!-- no translation found for vdm_secure_window (161700398158812314) -->
- <skip />
+ <string name="vdm_secure_window" msgid="161700398158812314">"Bạn không thể truy cập vào nội dung này trong khi phát trực tuyến. Hãy thử trên điện thoại."</string>
<string name="system_locale_title" msgid="711882686834677268">"Theo chế độ mặc định của hệ thống"</string>
<string name="default_card_name" msgid="9198284935962911468">"THẺ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 3bef669..066600b 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指纹操作已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"用户取消了指纹操作。"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"尝试次数过多,请稍后重试。"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"尝试次数过多。指纹传感器已停用。"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"请重试。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未注册任何指纹。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾选"</string>
<string name="selected" msgid="6614607926197755875">"已选择"</string>
<string name="not_selected" msgid="410652016565864475">"未选择"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 星,最高 {max} 星}other{# 星,最高 {max} 星}}"</string>
<string name="in_progress" msgid="2149208189184319441">"进行中"</string>
<string name="whichApplication" msgid="5432266899591255759">"选择要使用的应用:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"使用%1$s完成操作"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"区域偏好设置"</string>
<string name="search_language_hint" msgid="7004225294308793583">"输入语言名称"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"建议语言"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"推荐地区"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"建议的语言"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"建议的地区"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"所有语言"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"允许“<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>”访问所有设备日志吗?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"允许访问一次"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允许"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"设备日志会记录设备上发生的活动。应用可以使用这些日志查找和修复问题。\n\n部分日志可能包含敏感信息,因此请仅允许您信任的应用访问所有设备日志。\n\n如果您不授予此应用访问所有设备日志的权限,它仍然可以访问自己的日志。您的设备制造商可能仍然能够访问设备上的部分日志或信息。"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"不再显示"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"“<xliff:g id="APP_0">%1$s</xliff:g>”想要显示“<xliff:g id="APP_2">%2$s</xliff:g>”图块"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"编辑"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index e541a33..a1c0921f 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋操作已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋操作。"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"嘗試次數過多,請稍後再試。"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"嘗試次數過多,指紋感應器已停用。"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"再試一次。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未註冊任何指紋"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
<string name="selected" msgid="6614607926197755875">"揀咗"</string>
<string name="not_selected" msgid="410652016565864475">"未揀"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 顆星 (滿分為 {max} 顆星)}other{# 顆星 (滿分為 {max} 顆星)}}"</string>
<string name="in_progress" msgid="2149208189184319441">"處理中"</string>
<string name="whichApplication" msgid="5432266899591255759">"完成操作需使用"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"地區偏好設定"</string>
<string name="search_language_hint" msgid="7004225294308793583">"輸入語言名稱"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"建議"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"建議的語言"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"建議的語言"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"建議地區"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"所有語言"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"要允許「<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>」存取所有裝置記錄嗎?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"允許存取一次"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允許"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"裝置記錄會記下裝置上的活動。應用程式可透過這些記錄找出並修正問題。\n\n部分記錄可能包含敏感資料,因此請只允許信任的應用程式存取所有裝置記錄。\n\n如果不允許此應用程式存取所有裝置記錄,此應用程式仍能存取自己的記錄,且裝置製造商可能仍可存取裝置上的部分記錄或資料。"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"不要再顯示"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"「<xliff:g id="APP_0">%1$s</xliff:g>」想顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的快訊"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"編輯"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 81c68e6..cb8f7bf 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋作業已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋驗證作業。"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"嘗試次數過多,請稍後再試。"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"嘗試次數過多,指紋感應器已停用。"</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"請再試一次。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未登錄任何指紋。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
<string name="selected" msgid="6614607926197755875">"已選取"</string>
<string name="not_selected" msgid="410652016565864475">"未選取"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 顆星 (滿分為 {max} 顆星)}other{# 顆星 (滿分為 {max} 顆星)}}"</string>
<string name="in_progress" msgid="2149208189184319441">"處理中"</string>
<string name="whichApplication" msgid="5432266899591255759">"選擇要使用的應用程式"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"地區偏好設定"</string>
<string name="search_language_hint" msgid="7004225294308793583">"請輸入語言名稱"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"建議語言"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"建議的語言"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"建議語言"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"建議地區"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"所有語言"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"要允許「<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>」存取所有裝置記錄嗎?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"允許一次性存取"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允許"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"系統會透過裝置記錄記下裝置上的活動。應用程式可以根據這些記錄找出問題並進行修正。\n\n某些記錄可能含有機密資訊,因此請勿讓不信任的應用程式存取所有裝置記錄。\n\n即使你不允許這個應用程式存取所有裝置記錄,這個應用程式仍能存取自己的記錄,而且裝置製造商或許仍可存取裝置的某些記錄或資訊。"</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"不要再顯示"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"「<xliff:g id="APP_0">%1$s</xliff:g>」想要顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的區塊"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"編輯"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 8c115af..5ae40dc 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -605,7 +605,8 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Ukusebenza kwezigxivizo zeminwe kukhanseliwe."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Umsebenzi wezigxivizo zomunwe ukhanselwe umsebenzisi."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Imizamo eminingi kakhulu. Inzwa yezigxivizo zeminwe ikhutshaziwe."</string>
+ <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
+ <skip />
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Zama futhi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Azikho izigxivizo zeminwe ezibhalisiwe."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string>
@@ -1167,8 +1168,7 @@
<string name="not_checked" msgid="7972320087569023342">"akuhloliwe"</string>
<string name="selected" msgid="6614607926197755875">"okukhethiwe"</string>
<string name="not_selected" msgid="410652016565864475">"akukhethiwe"</string>
- <!-- no translation found for rating_label (1837085249662154601) -->
- <skip />
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Inkanyezi eyodwa kwezingu-{max}}one{Izinkanyezi ezingu-# kwezingu-{max}}other{Izinkanyezi ezingu-# kwezingu-{max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"kuyaqhubeka"</string>
<string name="whichApplication" msgid="5432266899591255759">"Qedela isenzo usebenzisa"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Qedela isenzo usebenzisa i-%1$s"</string>
@@ -1925,8 +1925,7 @@
<string name="country_selection_title" msgid="5221495687299014379">"Okuncamelayo kwesifunda"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Thayipha igama lolimi"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Okuphakanyisiwe"</string>
- <!-- no translation found for language_picker_regions_section_suggested (6080131515268225316) -->
- <skip />
+ <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Okuphakanyisiwe"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Izilimi eziphakanyisiwe"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Izifunda eziphakanyisiwe"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Zonke izilimi"</string>
@@ -2051,8 +2050,7 @@
<string name="log_access_confirmation_title" msgid="2343578467290592708">"Vumela i-<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ukuba ifinyelele wonke amalogu edivayisi?"</string>
<string name="log_access_confirmation_allow" msgid="5302517782599389507">"Vumela ukufinyelela kwesikhathi esisodwa"</string>
<string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ungavumeli"</string>
- <!-- no translation found for log_access_confirmation_body (1806692062668620735) -->
- <skip />
+ <string name="log_access_confirmation_body" msgid="1806692062668620735">"Amalogu edivayisi arekhoda okwenzekayo kudivayisi yakho. Ama-app angasebenzisa lawa malogu ukuze athole futhi alungise izinkinga.\n\nAmanye amalogu angase aqukathe ulwazi olubucayi, ngakho vumela ama-app owathembayo kuphela ukuthi afinyelele wonke amalogu edivayisi. \n\nUma ungayivumeli le app ukuthi ifinyelele wonke amalogu wedivayisi, isengakwazi ukufinyelela amalogu wayo. Umkhiqizi wedivayisi yakho usengakwazi ukufinyelela amanye amalogu noma ulwazi kudivayisi yakho."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ungabonisi futhi"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"I-<xliff:g id="APP_0">%1$s</xliff:g> ifuna ukubonisa izingcezu ze-<xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Hlela"</string>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 5fd9dc0..fe296c7 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -30,5 +30,4 @@
lockscreen, setting this to true should come with customized drawables. -->
<bool name="use_lock_pattern_drawable">false</bool>
<bool name="resolver_landscape_phone">true</bool>
- <bool name="system_server_plays_face_haptics">true</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d77fc9b..f685415 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1567,8 +1567,7 @@
<bool name="config_enableIdleScreenBrightnessMode">false</bool>
<!-- Array of desired screen brightness in nits corresponding to the lux values
- in the config_autoBrightnessLevels array. As with config_screenBrightnessMinimumNits and
- config_screenBrightnessMaximumNits, the display brightness is defined as the measured
+ in the config_autoBrightnessLevels array. The display brightness is defined as the measured
brightness of an all-white image.
If this is defined then:
@@ -1589,7 +1588,7 @@
<array name="config_autoBrightnessDisplayValuesNitsIdle">
</array>
- <!-- Array of output values for button backlight corresponding to the luX values
+ <!-- Array of output values for button 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.
@@ -2017,6 +2016,10 @@
<!-- The default volume for the ring stream -->
<integer name="config_audio_ring_vol_default">5</integer>
+ <!-- The default value for whether head tracking for
+ spatial audio is enabled for a newly connected audio device -->
+ <bool name="config_spatial_audio_head_tracking_enabled_default">false</bool>
+
<!-- Flag indicating whether platform level volume adjustments are enabled for remote sessions
on grouped devices. -->
<bool name="config_volumeAdjustmentForRemoteGroupSessions">true</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8488b68..ef33a22 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1797,7 +1797,7 @@
<!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
<string name="face_acquired_too_bright">Too bright. Try gentler lighting.</string>
<!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] -->
- <string name="face_acquired_too_dark">Try brighter lighting</string>
+ <string name="face_acquired_too_dark">Not enough light</string>
<!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] -->
<string name="face_acquired_too_close">Move phone farther away</string>
<!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b3492f1..e5db96a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -79,6 +79,11 @@
<java-symbol type="id" name="deny_button" />
<java-symbol type="id" name="description" />
<java-symbol type="id" name="divider" />
+ <java-symbol type="id" name="drag" />
+ <java-symbol type="id" name="profile_pager" />
+ <java-symbol type="id" name="chooser_header" />
+ <java-symbol type="id" name="content_preview_container" />
+ <java-symbol type="id" name="profile_tabhost" />
<java-symbol type="id" name="edit_query" />
<java-symbol type="id" name="edittext_container" />
<java-symbol type="id" name="expand_activities_button" />
@@ -282,6 +287,7 @@
<java-symbol type="integer" name="config_audio_notif_vol_steps" />
<java-symbol type="integer" name="config_audio_ring_vol_default" />
<java-symbol type="integer" name="config_audio_ring_vol_steps" />
+ <java-symbol type="bool" name="config_spatial_audio_head_tracking_enabled_default" />
<java-symbol type="bool" name="config_avoidGfxAccel" />
<java-symbol type="bool" name="config_bluetooth_address_validation" />
<java-symbol type="integer" name="config_chooser_max_targets_per_row" />
@@ -4852,6 +4858,5 @@
<java-symbol type="dimen" name="status_bar_height_default" />
- <java-symbol type="bool" name="system_server_plays_face_haptics" />
<java-symbol type="string" name="default_card_name"/>
</resources>
diff --git a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
index 2a3da05..625c66a 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
@@ -17,9 +17,11 @@
package android.app;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
import android.os.Parcel;
import android.test.AndroidTestCase;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -70,4 +72,18 @@
assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH,
fromParcel.getDescription().length());
}
+
+ @Test
+ public void testNullableFields() {
+ NotificationChannelGroup group = new NotificationChannelGroup("my_group_01", null);
+
+ Parcel parcel = Parcel.obtain();
+ group.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ NotificationChannelGroup fromParcel =
+ NotificationChannelGroup.CREATOR.createFromParcel(parcel);
+ assertEquals(group.getId(), fromParcel.getId());
+ assertTrue(TextUtils.isEmpty(fromParcel.getName()));
+ }
}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index a2d4baf..4011933 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -33,6 +33,7 @@
import android.app.Activity;
import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
+import android.app.Application;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.ResourcesManager;
@@ -46,6 +47,7 @@
import android.app.servertransaction.StopActivityItem;
import android.content.Context;
import android.content.Intent;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -178,6 +180,85 @@
}
@Test
+ public void testOverrideScale() throws Exception {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+ final Application app = activity.getApplication();
+ final ActivityThread activityThread = activity.getActivityThread();
+ final IApplicationThread appThread = activityThread.getApplicationThread();
+ final DisplayMetrics originalAppMetrics = new DisplayMetrics();
+ originalAppMetrics.setTo(app.getResources().getDisplayMetrics());
+ final Configuration originalAppConfig =
+ new Configuration(app.getResources().getConfiguration());
+ final DisplayMetrics originalActivityMetrics = new DisplayMetrics();
+ originalActivityMetrics.setTo(activity.getResources().getDisplayMetrics());
+ final Configuration originalActivityConfig =
+ new Configuration(activity.getResources().getConfiguration());
+
+ final Configuration newConfig = new Configuration(originalAppConfig);
+ newConfig.seq = BASE_SEQ + 1;
+ newConfig.smallestScreenWidthDp++;
+
+ final float originalScale = CompatibilityInfo.getOverrideInvertedScale();
+ float scale = 0.5f;
+ CompatibilityInfo.setOverrideInvertedScale(scale);
+ try {
+ // Send process level config change.
+ ClientTransaction transaction = newTransaction(activityThread, null);
+ transaction.addCallback(ConfigurationChangeItem.obtain(new Configuration(newConfig)));
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertScreenScale(scale, app, originalAppConfig, originalAppMetrics);
+ // The activity's config doesn't change because ConfigurationChangeItem is process level
+ // that won't affect activity's override config.
+ assertEquals(originalActivityConfig.densityDpi,
+ activity.getResources().getConfiguration().densityDpi);
+
+ scale = 0.8f;
+ CompatibilityInfo.setOverrideInvertedScale(scale);
+ // Send activity level config change.
+ newConfig.seq++;
+ newConfig.smallestScreenWidthDp++;
+ transaction = newTransaction(activityThread, activity.getActivityToken());
+ transaction.addCallback(ActivityConfigurationChangeItem.obtain(
+ new Configuration(newConfig)));
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertScreenScale(scale, activity, originalActivityConfig, originalActivityMetrics);
+ } finally {
+ CompatibilityInfo.setOverrideInvertedScale(originalScale);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> restoreConfig(activityThread, originalAppConfig));
+ }
+ assertScreenScale(originalScale, app, originalAppConfig, originalAppMetrics);
+ }
+
+ private static void assertScreenScale(float scale, Context context,
+ Configuration origConfig, DisplayMetrics origMetrics) {
+ final int expectedDpi = (int) (origConfig.densityDpi * scale + .5f);
+ final float expectedDensity = origMetrics.density * scale;
+ final int expectedWidthPixels = (int) (origMetrics.widthPixels * scale + .5f);
+ final int expectedHeightPixels = (int) (origMetrics.heightPixels * scale + .5f);
+ final Configuration expectedConfig = new Configuration(origConfig);
+ CompatibilityInfo.scaleConfiguration(scale, expectedConfig);
+ final Rect expectedBounds = expectedConfig.windowConfiguration.getBounds();
+ final Rect expectedAppBounds = expectedConfig.windowConfiguration.getAppBounds();
+ final Rect expectedMaxBounds = expectedConfig.windowConfiguration.getMaxBounds();
+
+ final Configuration currentConfig = context.getResources().getConfiguration();
+ final DisplayMetrics currentMetrics = context.getResources().getDisplayMetrics();
+ assertEquals(expectedDpi, currentConfig.densityDpi);
+ assertEquals(expectedDpi, currentMetrics.densityDpi);
+ assertEquals(expectedDensity, currentMetrics.density, 0.001f);
+ assertEquals(expectedWidthPixels, currentMetrics.widthPixels);
+ assertEquals(expectedHeightPixels, currentMetrics.heightPixels);
+ assertEquals(expectedBounds, currentConfig.windowConfiguration.getBounds());
+ assertEquals(expectedAppBounds, currentConfig.windowConfiguration.getAppBounds());
+ assertEquals(expectedMaxBounds, currentConfig.windowConfiguration.getMaxBounds());
+ }
+
+ @Test
public void testHandleActivityConfigurationChanged() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
@@ -459,19 +540,17 @@
} finally {
// Make sure to reset the process config to prevent side effects to other
// tests.
- Configuration activityThreadConfig = activityThread.getConfiguration();
- activityThreadConfig.seq = originalAppConfig.seq - 1;
-
- Configuration resourceManagerConfig = ResourcesManager.getInstance()
- .getConfiguration();
- resourceManagerConfig.seq = originalAppConfig.seq - 1;
-
- activityThread.updatePendingConfiguration(originalAppConfig);
- activityThread.handleConfigurationChanged(originalAppConfig);
+ restoreConfig(activityThread, originalAppConfig);
}
});
}
+ private static void restoreConfig(ActivityThread thread, Configuration originalConfig) {
+ thread.getConfiguration().seq = originalConfig.seq - 1;
+ ResourcesManager.getInstance().getConfiguration().seq = originalConfig.seq - 1;
+ thread.handleConfigurationChanged(originalConfig);
+ }
+
@Test
public void testActivityOrientationChanged_DoesntOverrideVirtualDisplayOrientation() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 5b3be58..352c6a7 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -39,8 +39,11 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
/** Tests that ensure appropriate settings are backed up. */
@Presubmit
@@ -710,13 +713,12 @@
@Test
public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException {
- final CountDownLatch countDownLatch = new CountDownLatch(1);
+ final AtomicReference<Properties> changedProperties = new AtomicReference<>();
+ final CompletableFuture<Boolean> completed = new CompletableFuture<>();
DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
- assertThat(properties.getKeyset()).contains(KEY);
- assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
- countDownLatch.countDown();
+ changedProperties.set(properties);
+ completed.complete(true);
};
try {
@@ -724,9 +726,15 @@
Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(),
changeListener);
DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
- assertThat(countDownLatch.await(
+
+ assertThat(completed.get(
WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
- } catch (InterruptedException e) {
+
+ Properties properties = changedProperties.get();
+ assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+ assertThat(properties.getKeyset()).contains(KEY);
+ assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
+ } catch (ExecutionException | TimeoutException | InterruptedException e) {
Assert.fail(e.getMessage());
} finally {
DeviceConfig.removeOnPropertiesChangedListener(changeListener);
@@ -735,7 +743,6 @@
@Test
public void onPropertiesChangedListener_setPropertiesCallback() {
- final CountDownLatch countDownLatch = new CountDownLatch(1);
DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
@@ -744,16 +751,12 @@
keyValues.put(KEY3, VALUE3);
Properties setProperties = new Properties(NAMESPACE, keyValues);
+ final AtomicReference<Properties> changedProperties = new AtomicReference<>();
+ final CompletableFuture<Boolean> completed = new CompletableFuture<>();
+
DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
- assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
- // KEY updated from VALUE to VALUE2
- assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2);
- // KEY2 deleted (returns default_value)
- assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value");
- //KEY3 added with VALUE3
- assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3);
- countDownLatch.countDown();
+ changedProperties.set(properties);
+ completed.complete(true);
};
try {
@@ -761,9 +764,28 @@
Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(),
changeListener);
DeviceConfig.setProperties(setProperties);
- assertThat(countDownLatch.await(
+
+ assertThat(completed.get(
WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
- } catch (InterruptedException | DeviceConfig.BadConfigException e) {
+
+ if (changedProperties.get().getKeyset().size() != 3) {
+ // Sometimes there are a few onChanged callback calls. Let's wait a bit more.
+ final int oneMoreOnChangedDelayMs = 100;
+ Thread.currentThread().sleep(oneMoreOnChangedDelayMs);
+ }
+
+ Properties properties = changedProperties.get();
+ assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
+ // KEY updated from VALUE to VALUE2
+ assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2);
+ // KEY2 deleted (returns default_value)
+ assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value");
+ // KEY3 added with VALUE3
+ assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3);
+ } catch (ExecutionException | TimeoutException | InterruptedException e) {
+ Assert.fail(e.getMessage());
+ } catch (DeviceConfig.BadConfigException e) {
Assert.fail(e.getMessage());
} finally {
DeviceConfig.removeOnPropertiesChangedListener(changeListener);
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritableViewInfoTest.java b/core/tests/coretests/src/android/view/stylus/HandwritableViewInfoTest.java
index 4bea54b..5933d54 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritableViewInfoTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritableViewInfoTest.java
@@ -70,7 +70,8 @@
@Test
public void update_viewDisableAutoHandwriting() {
final Rect rect = new Rect(1, 2, 3, 4);
- final View view = HandwritingTestUtil.createView(rect, false /* autoHandwritingEnabled */);
+ final View view = HandwritingTestUtil.createView(rect, false /* autoHandwritingEnabled */,
+ true /* isStylusHandwritingAvailable */);
final HandwritingInitiator.HandwritableViewInfo handwritableViewInfo =
new HandwritingInitiator.HandwritableViewInfo(view);
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 18da1a4..d4a6632 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -16,7 +16,6 @@
package android.view.stylus;
-import static android.provider.Settings.Global.STYLUS_HANDWRITING_ENABLED;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
@@ -33,7 +32,6 @@
import android.content.Context;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
import android.view.HandwritingInitiator;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -45,15 +43,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.compatibility.common.util.PollingCheck;
-
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.concurrent.TimeUnit;
-
/**
* Tests for {@link HandwritingInitiator}
*
@@ -69,8 +62,6 @@
private static final int HW_BOUNDS_OFFSETS_TOP_PX = 20;
private static final int HW_BOUNDS_OFFSETS_RIGHT_PX = 30;
private static final int HW_BOUNDS_OFFSETS_BOTTOM_PX = 40;
- private static final int SETTING_VALUE_ON = 1;
- private static final int SETTING_VALUE_OFF = 0;
private int mHandwritingSlop = 4;
private static final Rect sHwArea = new Rect(100, 200, 500, 500);
@@ -78,29 +69,12 @@
private HandwritingInitiator mHandwritingInitiator;
private View mTestView;
private Context mContext;
- private int mHwInitialState;
- private boolean mShouldRestoreInitialHwState;
@Before
public void setup() throws Exception {
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mContext = instrumentation.getTargetContext();
- mHwInitialState = Settings.Global.getInt(mContext.getContentResolver(),
- STYLUS_HANDWRITING_ENABLED, SETTING_VALUE_OFF);
- if (mHwInitialState != SETTING_VALUE_ON) {
- Settings.Global.putInt(mContext.getContentResolver(),
- STYLUS_HANDWRITING_ENABLED, SETTING_VALUE_ON);
- mShouldRestoreInitialHwState = true;
- }
- String imeId = HandwritingImeService.getImeId();
- instrumentation.getUiAutomation().executeShellCommand("ime enable " + imeId);
- instrumentation.getUiAutomation().executeShellCommand("ime set " + imeId);
- PollingCheck.check("Check that stylus handwriting is available",
- TimeUnit.SECONDS.toMillis(10),
- () -> mContext.getSystemService(InputMethodManager.class)
- .isStylusHandwritingAvailable());
-
final ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext);
mHandwritingSlop = viewConfiguration.getScaledHandwritingSlop();
@@ -108,7 +82,8 @@
mHandwritingInitiator =
spy(new HandwritingInitiator(viewConfiguration, inputMethodManager));
- mTestView = createView(sHwArea, true,
+ mTestView = createView(sHwArea, true /* autoHandwritingEnabled */,
+ true /* isStylusHandwritingAvailable */,
HW_BOUNDS_OFFSETS_LEFT_PX,
HW_BOUNDS_OFFSETS_TOP_PX,
HW_BOUNDS_OFFSETS_RIGHT_PX,
@@ -116,17 +91,6 @@
mHandwritingInitiator.updateHandwritingAreasForView(mTestView);
}
- @After
- public void tearDown() throws Exception {
- if (mShouldRestoreInitialHwState) {
- mShouldRestoreInitialHwState = false;
- Settings.Global.putInt(mContext.getContentResolver(),
- STYLUS_HANDWRITING_ENABLED, mHwInitialState);
- }
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .executeShellCommand("ime reset");
- }
-
@Test
public void onTouchEvent_startHandwriting_when_stylusMoveOnce_withinHWArea() {
mHandwritingInitiator.onInputConnectionCreated(mTestView);
@@ -244,17 +208,15 @@
}
@Test
- public void onTouchEvent_notStartHandwriting_whenHandwritingNotAvailable() throws Exception {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .executeShellCommand("ime reset");
- PollingCheck.check("Check that stylus handwriting is unavailable",
- TimeUnit.SECONDS.toMillis(10),
- () -> !mContext.getSystemService(InputMethodManager.class)
- .isStylusHandwritingAvailable());
+ public void onTouchEvent_notStartHandwriting_whenHandwritingNotAvailable() {
+ final Rect rect = new Rect(600, 600, 900, 900);
+ final View testView = createView(rect, true /* autoHandwritingEnabled */,
+ false /* isStylusHandwritingAvailable */);
+ mHandwritingInitiator.updateHandwritingAreasForView(testView);
- mHandwritingInitiator.onInputConnectionCreated(mTestView);
- final int x1 = (sHwArea.left + sHwArea.right) / 2;
- final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
+ mHandwritingInitiator.onInputConnectionCreated(testView);
+ final int x1 = (rect.left + rect.right) / 2;
+ final int y1 = (rect.top + rect.bottom) / 2;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
mHandwritingInitiator.onTouchEvent(stylusEvent1);
@@ -356,7 +318,8 @@
@Test
public void autoHandwriting_whenDisabled_wontStartHW() {
- View mockView = createView(sHwArea, false);
+ View mockView = createView(sHwArea, false /* autoHandwritingEnabled */,
+ true /* isStylusHandwritingAvailable */);
mHandwritingInitiator.onInputConnectionCreated(mockView);
final int x1 = (sHwArea.left + sHwArea.right) / 2;
final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java b/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
index e6e50e9..388a996 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
@@ -29,14 +29,18 @@
public class HandwritingTestUtil {
public static View createView(Rect handwritingArea) {
- return createView(handwritingArea, true);
- }
-
- public static View createView(Rect handwritingArea, boolean autoHandwritingEnabled) {
- return createView(handwritingArea, autoHandwritingEnabled, 0, 0, 0, 0);
+ return createView(handwritingArea, true /* autoHandwritingEnabled */,
+ true /* isStylusHandwritingAvailable */);
}
public static View createView(Rect handwritingArea, boolean autoHandwritingEnabled,
+ boolean isStylusHandwritingAvailable) {
+ return createView(handwritingArea, autoHandwritingEnabled, isStylusHandwritingAvailable,
+ 0, 0, 0, 0);
+ }
+
+ public static View createView(Rect handwritingArea, boolean autoHandwritingEnabled,
+ boolean isStylusHandwritingAvailable,
float handwritingBoundsOffsetLeft, float handwritingBoundsOffsetTop,
float handwritingBoundsOffsetRight, float handwritingBoundsOffsetBottom) {
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
@@ -57,6 +61,7 @@
View view = spy(new View(context));
when(view.isAttachedToWindow()).thenReturn(true);
when(view.isAggregatedVisible()).thenReturn(true);
+ when(view.isStylusHandwritingAvailable()).thenReturn(isStylusHandwritingAvailable);
when(view.getHandwritingArea()).thenReturn(handwritingArea);
when(view.getHandwritingBoundsOffsetLeft()).thenReturn(handwritingBoundsOffsetLeft);
when(view.getHandwritingBoundsOffsetTop()).thenReturn(handwritingBoundsOffsetTop);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
new file mode 100644
index 0000000..f027e0b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.internal.app
+
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.service.chooser.ChooserTarget
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.R
+import com.android.internal.app.chooser.SelectableTargetInfo
+import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator
+import com.android.server.testutils.any
+import com.android.server.testutils.mock
+import com.android.server.testutils.whenever
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidJUnit4::class)
+class ChooserListAdapterTest {
+ private val packageManager = mock<PackageManager> {
+ whenever(resolveActivity(any(), anyInt())).thenReturn(mock())
+ }
+ private val context = InstrumentationRegistry.getInstrumentation().getContext()
+ private val resolverListController = mock<ResolverListController>()
+ private val chooserListCommunicator = mock<ChooserListAdapter.ChooserListCommunicator> {
+ whenever(maxRankedTargets).thenReturn(0)
+ }
+ private val selectableTargetInfoCommunicator =
+ mock<SelectableTargetInfoCommunicator> {
+ whenever(targetIntent).thenReturn(mock())
+ }
+ private val chooserActivityLogger = mock<ChooserActivityLogger>()
+
+ private val testSubject = ChooserListAdapter(
+ context,
+ emptyList(),
+ emptyArray(),
+ emptyList(),
+ false,
+ resolverListController,
+ chooserListCommunicator,
+ selectableTargetInfoCommunicator,
+ packageManager,
+ chooserActivityLogger
+ )
+
+ @Test
+ fun testDirectShareTargetLoadingIconIsStarted() {
+ val view = createView()
+ val viewHolder = ResolverListAdapter.ViewHolder(view)
+ view.tag = viewHolder
+ val targetInfo = createSelectableTargetInfo()
+ val iconTask = mock<ChooserListAdapter.LoadDirectShareIconTask> {
+ doNothing().whenever(this).loadIcon()
+ }
+ testSubject.setTestLoadDirectShareTaskProvider(
+ mock {
+ whenever(get()).thenReturn(iconTask)
+ }
+ )
+ testSubject.testViewBind(view, targetInfo, 0)
+
+ verify(iconTask, times(1)).loadIcon()
+ verify(iconTask, times(1)).setViewHolder(viewHolder)
+ }
+
+ @Test
+ fun testOnlyOneTaskPerTarget() {
+ val view = createView()
+ val viewHolderOne = ResolverListAdapter.ViewHolder(view)
+ view.tag = viewHolderOne
+ val targetInfo = createSelectableTargetInfo()
+ val iconTaskOne = mock<ChooserListAdapter.LoadDirectShareIconTask> {
+ doNothing().whenever(this).loadIcon()
+ }
+ val testTaskProvider = mock<ChooserListAdapter.LoadDirectShareIconTaskProvider> {
+ whenever(get()).thenReturn(iconTaskOne)
+ }
+ testSubject.setTestLoadDirectShareTaskProvider(
+ testTaskProvider
+ )
+ testSubject.testViewBind(view, targetInfo, 0)
+
+ val viewHolderTwo = ResolverListAdapter.ViewHolder(view)
+ view.tag = viewHolderTwo
+ whenever(testTaskProvider.get()).thenReturn(mock())
+
+ testSubject.testViewBind(view, targetInfo, 0)
+
+ verify(iconTaskOne, times(1)).loadIcon()
+ verify(iconTaskOne, times(1)).setViewHolder(viewHolderOne)
+ verify(iconTaskOne, times(1)).setViewHolder(viewHolderTwo)
+ verify(testTaskProvider, times(1)).get()
+ }
+
+ private fun createSelectableTargetInfo(): SelectableTargetInfo =
+ SelectableTargetInfo(
+ context,
+ null,
+ createChooserTarget(),
+ 1f,
+ selectableTargetInfoCommunicator,
+ null
+ )
+
+ private fun createChooserTarget(): ChooserTarget =
+ ChooserTarget(
+ "Title",
+ null,
+ 1f,
+ ComponentName("package", "package.Class"),
+ Bundle()
+ )
+
+ private fun createView(): View {
+ val view = FrameLayout(context)
+ TextView(context).apply {
+ id = R.id.text1
+ view.addView(this)
+ }
+ TextView(context).apply {
+ id = R.id.text2
+ view.addView(this)
+ }
+ ImageView(context).apply {
+ id = R.id.icon
+ view.addView(this)
+ }
+ return view
+ }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index dfff75b..682483d9 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -3217,12 +3217,6 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
- "956467125": {
- "message": "Reparenting Activity to embedded TaskFragment, but the Activity is not collected",
- "level": "WARN",
- "group": "WM_DEBUG_WINDOW_TRANSITIONS",
- "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
- },
"958338552": {
"message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index a8ab6d9..54d6428 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -670,8 +670,9 @@
/**
* @hide
*/
- public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
- nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
+ public void punchHole(float left, float top, float right, float bottom, float rx, float ry,
+ float alpha) {
+ nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha);
}
/**
@@ -823,5 +824,5 @@
float hOffset, float vOffset, int flags, long nativePaint);
private static native void nPunchHole(long renderer, float left, float top, float right,
- float bottom, float rx, float ry);
+ float bottom, float rx, float ry, float alpha);
}
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index d06f665..1ba79b8 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -610,8 +610,9 @@
* @hide
*/
@Override
- public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
- nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
+ public void punchHole(float left, float top, float right, float bottom, float rx, float ry,
+ float alpha) {
+ nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha);
}
@FastNative
@@ -742,5 +743,5 @@
@FastNative
private static native void nPunchHole(long renderer, float left, float top, float right,
- float bottom, float rx, float ry);
+ float bottom, float rx, float ry, float alpha);
}
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 5727b91..724a50f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -583,7 +583,7 @@
}
if (!isOnReparent && getContainerWithActivity(activity) == null
- && getInitialTaskFragmentToken(activity) != null) {
+ && getTaskFragmentTokenFromActivityClientRecord(activity) != null) {
// We can't find the new launched activity in any recorded container, but it is
// currently placed in an embedded TaskFragment. This can happen in two cases:
// 1. the activity is embedded in another app.
@@ -866,11 +866,12 @@
}
@VisibleForTesting
+ @GuardedBy("mLock")
void onActivityDestroyed(@NonNull Activity activity) {
// Remove any pending appeared activity, as the server won't send finished activity to the
// organizer.
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- mTaskContainers.valueAt(i).cleanupPendingAppearedActivity(activity);
+ mTaskContainers.valueAt(i).onActivityDestroyed(activity);
}
// We didn't trigger the callback if there were any pending appeared activities, so check
// again after the pending is removed.
@@ -1605,15 +1606,16 @@
}
/**
- * Gets the token of the initial TaskFragment that embedded this activity. Do not rely on it
- * after creation because the activity could be reparented.
+ * Gets the token of the TaskFragment that embedded this activity. It is available as soon as
+ * the activity is created and attached, so it can be used during {@link #onActivityCreated}
+ * before the server notifies the organizer to avoid racing condition.
*/
@VisibleForTesting
@Nullable
- IBinder getInitialTaskFragmentToken(@NonNull Activity activity) {
+ IBinder getTaskFragmentTokenFromActivityClientRecord(@NonNull Activity activity) {
final ActivityThread.ActivityClientRecord record = ActivityThread.currentActivityThread()
.getActivityClient(activity.getActivityToken());
- return record != null ? record.mInitialTaskFragmentToken : null;
+ return record != null ? record.mTaskFragmentToken : null;
}
/**
@@ -1691,7 +1693,8 @@
@Nullable Bundle savedInstanceState) {
synchronized (mLock) {
final IBinder activityToken = activity.getActivityToken();
- final IBinder initialTaskFragmentToken = getInitialTaskFragmentToken(activity);
+ final IBinder initialTaskFragmentToken =
+ getTaskFragmentTokenFromActivityClientRecord(activity);
// If the activity is not embedded, then it will not have an initial task fragment
// token so no further action is needed.
if (initialTaskFragmentToken == null) {
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 45645b2..b563677 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -137,6 +137,13 @@
return mContainers.isEmpty() && mFinishedContainer.isEmpty();
}
+ /** Called when the activity is destroyed. */
+ void onActivityDestroyed(@NonNull Activity activity) {
+ for (TaskFragmentContainer container : mContainers) {
+ container.onActivityDestroyed(activity);
+ }
+ }
+
/** Removes the pending appeared activity from all TaskFragments in this Task. */
void cleanupPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
for (TaskFragmentContainer container : mContainers) {
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 2843b14..626e0d9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.Activity;
+import android.app.ActivityThread;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Intent;
import android.graphics.Rect;
@@ -190,6 +191,19 @@
// Remove the pending activity from other TaskFragments.
mTaskContainer.cleanupPendingAppearedActivity(pendingAppearedActivity);
mPendingAppearedActivities.add(pendingAppearedActivity);
+ updateActivityClientRecordTaskFragmentToken(pendingAppearedActivity);
+ }
+
+ /**
+ * Updates the {@link ActivityThread.ActivityClientRecord#mTaskFragmentToken} for the
+ * activity. This makes sure the token is up-to-date if the activity is relaunched later.
+ */
+ private void updateActivityClientRecordTaskFragmentToken(@NonNull Activity activity) {
+ final ActivityThread.ActivityClientRecord record = ActivityThread
+ .currentActivityThread().getActivityClient(activity.getActivityToken());
+ if (record != null) {
+ record.mTaskFragmentToken = mToken;
+ }
}
void removePendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
@@ -197,8 +211,29 @@
}
void clearPendingAppearedActivities() {
+ final List<Activity> cleanupActivities = new ArrayList<>(mPendingAppearedActivities);
+ // Clear mPendingAppearedActivities so that #getContainerWithActivity won't return the
+ // current TaskFragment.
mPendingAppearedActivities.clear();
mPendingAppearedIntent = null;
+
+ // For removed pending activities, we need to update the them to their previous containers.
+ for (Activity activity : cleanupActivities) {
+ final TaskFragmentContainer curContainer = mController.getContainerWithActivity(
+ activity);
+ if (curContainer != null) {
+ curContainer.updateActivityClientRecordTaskFragmentToken(activity);
+ }
+ }
+ }
+
+ /** Called when the activity is destroyed. */
+ void onActivityDestroyed(@NonNull Activity activity) {
+ removePendingAppearedActivity(activity);
+ if (mInfo != null) {
+ // Remove the activity now because there can be a delay before the server callback.
+ mInfo.getActivities().remove(activity.getActivityToken());
+ }
}
@Nullable
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 4dbbc04..58870a6 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
@@ -930,7 +930,8 @@
@Test
public void testResolveActivityToContainer_inUnknownTaskFragment() {
- doReturn(new Binder()).when(mSplitController).getInitialTaskFragmentToken(mActivity);
+ doReturn(new Binder()).when(mSplitController)
+ .getTaskFragmentTokenFromActivityClientRecord(mActivity);
// No need to handle when the new launched activity is in an unknown TaskFragment.
assertTrue(mSplitController.resolveActivityToContainer(mTransaction, mActivity,
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 1bc81ee..082774e 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -316,6 +316,25 @@
assertEquals(activity, container.getBottomMostActivity());
}
+ @Test
+ public void testOnActivityDestroyed() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
+ mIntent, taskContainer, mController);
+ container.addPendingAppearedActivity(mActivity);
+ final List<IBinder> activities = new ArrayList<>();
+ activities.add(mActivity.getActivityToken());
+ doReturn(activities).when(mInfo).getActivities();
+ container.setInfo(mTransaction, mInfo);
+
+ assertTrue(container.hasActivity(mActivity.getActivityToken()));
+
+ taskContainer.onActivityDestroyed(mActivity);
+
+ // It should not contain the destroyed Activity.
+ assertFalse(container.hasActivity(mActivity.getActivityToken()));
+ }
+
/** Creates a mock activity in the organizer process. */
private Activity createMockActivity() {
final Activity activity = mock(Activity.class);
diff --git a/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml
new file mode 100644
index 0000000..a348b14
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/tv_window_menu_icon_size"
+ android:height="@dimen/tv_window_menu_icon_size"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M17,4h3c1.1,0 2,0.9 2,2v2h-2L20,6h-3L17,4zM4,8L4,6h3L7,4L4,4c-1.1,0 -2,0.9 -2,2v2h2zM20,16v2h-3v2h3c1.1,0 2,-0.9 2,-2v-2h-2zM7,18L4,18v-2L2,16v2c0,1.1 0.9,2 2,2h3v-2zM18,8L6,8v8h12L18,8z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml
new file mode 100644
index 0000000..c5d54c5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/tv_window_menu_icon_size"
+ android:height="@dimen/tv_window_menu_icon_size"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"/>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml b/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml
new file mode 100644
index 0000000..e0fa59c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml
@@ -0,0 +1,125 @@
+<!--
+ ~ 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.
+ -->
+<!-- Layout for TvSplitMenuView -->
+<com.android.wm.shell.splitscreen.tv.TvSplitMenuView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+
+ <LinearLayout
+ android:id="@+id/tv_split_main_menu"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_main_menu_focus_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/tv_split_menu_ic_focus" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_main_menu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_close_white" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_menu_swap_stages"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/tv_split_menu_ic_swap" />
+
+ <LinearLayout
+ android:id="@+id/tv_split_side_menu"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_side_menu_focus_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/tv_split_menu_ic_focus" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_side_menu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_close_white" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+</com.android.wm.shell.splitscreen.tv.TvSplitMenuView>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index a57ce9d..40c4c35 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sommige programme werk beter in portret"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Probeer een van hierdie opsies om jou spasie ten beste te benut"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Draai jou toestel om dit volskerm te maak"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dubbeltik langs ’n program om dit te herposisioneer"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Het dit"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vou uit vir meer inligting."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimeer"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index c1e1629..a183a0b 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"አንዳንድ መተግበሪያዎች በቁም ፎቶ ውስጥ በተሻለ ሁኔታ ይሰራሉ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ቦታዎን በአግባቡ ለመጠቀም ከእነዚህ አማራጮች ውስጥ አንዱን ይሞክሩ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ወደ የሙሉ ገጽ ዕይታ ለመሄድ መሣሪያዎን ያሽከርክሩት"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ቦታውን ለመቀየር ከመተግበሪያው ቀጥሎ ላይ ሁለቴ መታ ያድርጉ"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ገባኝ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ለተጨማሪ መረጃ ይዘርጉ።"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"አስፋ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 9a5a413..98f11dd 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"تعمل بعض التطبيقات على أكمل وجه في الشاشات العمودية"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"جرِّب تنفيذ أحد هذه الخيارات للاستفادة من مساحتك إلى أقصى حد."</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"قم بتدوير الشاشة للانتقال إلى وضع ملء الشاشة."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"انقر مرتين بجانب التطبيق لتغيير موضعه."</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"حسنًا"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"التوسيع للحصول على مزيد من المعلومات"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"تكبير"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index da3d3da..18cb3ff 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"কিছুমান এপে প’ৰ্ট্ৰেইট ম’ডত বেছি ভালকৈ কাম কৰে"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"আপোনাৰ spaceৰ পৰা পাৰ্যমানে উপকৃত হ’বলৈ ইয়াৰে এটা বিকল্প চেষ্টা কৰি চাওক"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"পূৰ্ণ স্ক্ৰীনলৈ যাবলৈ আপোনাৰ ডিভাইচটো ঘূৰাওক"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"এপ্টোৰ স্থান সলনি কৰিবলৈ ইয়াৰ কাষত দুবাৰ টিপক"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"বুজি পালোঁ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"অধিক তথ্যৰ বাবে বিস্তাৰ কৰক।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"সৰ্বাধিক মাত্ৰালৈ বঢ়াওক"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index bd7f8f0..2040288 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Bəzi tətbiqlər portret rejimində daha yaxşı işləyir"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Məkanınızdan maksimum yararlanmaq üçün bu seçimlərdən birini sınayın"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Tam ekrana keçmək üçün cihazınızı fırladın"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tətbiqin yerini dəyişmək üçün yanına iki dəfə toxunun"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ətraflı məlumat üçün genişləndirin."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Böyüdün"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 114f791..b2b484e 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Neke aplikacije najbolje funkcionišu u uspravnom režimu"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da biste na najbolji način iskoristili prostor"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotirajte uređaj za prikaz preko celog ekrana"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da biste promenili njenu poziciju"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Važi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za još informacija."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Uvećajte"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 6ae5e42..59db992 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Некаторыя праграмы лепш за ўсё працуюць у кніжнай арыентацыі"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Каб эфектыўна выкарыстоўваць прастору, паспрабуйце адзін з гэтых варыянтаў"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Каб перайсці ў поўнаэкранны рэжым, павярніце прыладу"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Двойчы націсніце побач з праграмай, каб перамясціць яе"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Зразумела"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Разгарнуць для дадатковай інфармацыі"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Разгарнуць"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index b11a8d4..8b5c4a9 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Някои приложения работят най-добре във вертикален режим"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Изпробвайте една от следните опции, за да се възползвате максимално от мястото на екрана"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Завъртете екрана си, за да преминете в режим на цял екран"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Докоснете два пъти дадено приложение, за да промените позицията му"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Разбрах"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Разгъване за още информация."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Увеличаване"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 6eab036..d7ff018 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"কিছু অ্যাপ \'পোর্ট্রেট\' মোডে সবচেয়ে ভাল কাজ করে"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"আপনার স্পেস সবচেয়ে ভালভাবে কাজে লাগাতে এইসব বিকল্পের মধ্যে কোনও একটি ব্যবহার করে দেখুন"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"\'ফুল স্ক্রিন\' মোডে যেতে ডিভাইস ঘোরান"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"কোনও অ্যাপের পাশে ডবল ট্যাপ করে সেটির জায়গা পরিবর্তন করুন"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"বুঝেছি"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"আরও তথ্যের জন্য বড় করুন।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"বড় করুন"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 8420b53..f4c4560 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Određene aplikacije najbolje funkcioniraju u uspravnom načinu rada"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da maksimalno iskoristite prostor"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zarotirajte uređaj da aktivirate prikaz preko cijelog ekrana"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da promijenite njen položaj"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Razumijem"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za više informacija."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziranje"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index d4c1eea..c4e6b0d 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunes aplicacions funcionen millor en posició vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prova una d\'aquestes opcions per treure el màxim profit de l\'espai"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gira el dispositiu per passar a pantalla completa"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Fes doble toc al costat d\'una aplicació per canviar-ne la posició"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entesos"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Desplega per obtenir més informació."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximitza"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 00034a6..8b0d1ff 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Některé aplikace fungují nejlépe na výšku"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pokud chcete maximálně využít prostor, vyzkoušejte jednu z těchto možností"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Otočením zařízení přejděte do režimu celé obrazovky"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvojitým klepnutím vedle aplikace změňte její umístění"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozbalením zobrazíte další informace."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovat"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 0dd1e1f..94faf41 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Nogle apps fungerer bedst i stående format"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prøv én af disse muligheder for at få mest muligt ud af dit rum"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Drej din enhed for at gå til fuld skærm"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tryk to gange ud for en app for at ændre dens placering"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Udvid for at få flere oplysninger."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimér"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index ff7fed9..3971445 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -78,14 +78,15 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Einige Apps funktionieren am besten im Hochformat"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Mithilfe dieser Möglichkeiten kannst du dein Display optimal nutzen"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gerät drehen, um zum Vollbildmodus zu wechseln"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Neben einer App doppeltippen, um die Position zu ändern"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Für weitere Informationen maximieren."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximieren"</string>
- <!-- no translation found for minimize_button_text (271592547935841753) -->
- <skip />
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimieren"</string>
<string name="close_button_text" msgid="2913281996024033299">"Schließen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 634b446..023c2d6 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Ορισμένες εφαρμογές λειτουργούν καλύτερα σε κατακόρυφο προσανατολισμό"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Δοκιμάστε μία από αυτές τις επιλογές για να αξιοποιήσετε στο έπακρο τον χώρο σας."</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Περιστρέψτε τη συσκευή σας για μετάβαση σε πλήρη οθόνη."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Πατήστε δύο φορές δίπλα σε μια εφαρμογή για να αλλάξετε τη θέση της."</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Το κατάλαβα"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ανάπτυξη για περισσότερες πληροφορίες."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Μεγιστοποίηση"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 431882f..acc75e4 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -78,10 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 431882f..acc75e4 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -78,10 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 431882f..acc75e4 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -78,10 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 431882f..acc75e4 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -78,10 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 87b04e0..4e9f13f 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -78,10 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximize"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 759fd4d..b5b6670 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunas apps funcionan mejor en modo vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prueba estas opciones para aprovechar al máximo tu espacio"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rota el dispositivo para ver la pantalla completa"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Presiona dos veces junto a una app para cambiar su posición"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expande para obtener más información."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index b2440bf..e38fc09 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunas aplicaciones funcionan mejor en vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prueba una de estas opciones para sacar el máximo partido al espacio de tu pantalla"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gira el dispositivo para ir al modo de pantalla completa"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toca dos veces junto a una aplicación para cambiar su posición"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mostrar más información"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 60c29f8..f468452 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Mõni rakendus töötab kõige paremini vertikaalpaigutuses"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Proovige ühte neist valikutest, et oma ruumi parimal moel kasutada"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pöörake seadet, et aktiveerida täisekraanirežiim"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Topeltpuudutage rakenduse kõrval, et selle asendit muuta"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Selge"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Laiendage lisateabe saamiseks."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimeeri"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 6b0a88c..2c1ee82 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Aplikazio batzuk orientazio bertikalean funtzionatzen dute hobekien"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pantailako eremuari ahalik eta etekinik handiena ateratzeko, probatu aukera hauetakoren bat"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pantaila osoko modua erabiltzeko, biratu gailua"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren ondoko edozein toki"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ados"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Informazio gehiago lortzeko, zabaldu hau."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizatu"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 0a01b36..6b7bf4d 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه ضربه بزنید"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن ضربه بزنید"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن ضربه بزنید."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"برخیاز برنامهها در حالت عمودی عملکرد بهتری دارند"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"با امتحان کردن یکی از این گزینهها، بیشترین بهره را از فضایتان ببرید"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"برای رفتن به حالت تمام صفحه، دستگاهتان را بچرخانید"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"در کنار برنامه دوضربه بزنید تا جابهجا شود"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"متوجهام"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"برای اطلاعات بیشتر، گسترده کنید."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"بزرگ کردن"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 6a7ecf1..b82a7cc 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Osa sovelluksista toimii parhaiten pystytilassa"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Kokeile jotakin näistä vaihtoehdoista, jotta saat parhaan hyödyn näytön tilasta"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Käännä laitetta, niin se siirtyy koko näytön tilaan"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Kaksoisnapauta sovellusta, jos haluat siirtää sitä"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Katso lisätietoja laajentamalla."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Suurenna"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 5a7c505..449dc27 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Certaines applications fonctionnent mieux en mode portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Essayez l\'une de ces options pour tirer le meilleur parti de votre espace"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Faites pivoter votre appareil pour passer en plein écran"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Touchez deux fois à côté d\'une application pour la repositionner"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développer pour en savoir plus."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 7b1203e..15148b7 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Certaines applis fonctionnent mieux en mode Portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Essayez l\'une de ces options pour exploiter pleinement l\'espace"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Faites pivoter l\'appareil pour passer en plein écran"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Appuyez deux fois à côté d\'une appli pour la repositionner"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développez pour obtenir plus d\'informations"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index afb8a1e..b848fd0 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunhas aplicacións funcionan mellor en modo vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Proba unha destas opcións para sacar o máximo proveito do espazo da pantalla"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Xira o dispositivo para ver o contido en pantalla completa"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toca dúas veces a carón dunha aplicación para cambiala de posición"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Despregar para obter máis información."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 549c97a..79d27a2 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"અમુક ઍપ પોર્ટ્રેટ મોડમાં શ્રેષ્ઠ રીતે કાર્ય કરે છે"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"તમારી સ્પેસનો વધુને વધુ લાભ લેવા માટે, આ વિકલ્પોમાંથી કોઈ એક અજમાવો"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"પૂર્ણ સ્ક્રીન મોડ લાગુ કરવા માટે, તમારા ડિવાઇસને ફેરવો"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"કોઈ ઍપની જગ્યા બદલવા માટે, તેની બાજુમાં બે વાર ટૅપ કરો"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"સમજાઈ ગયું"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"વધુ માહિતી માટે મોટું કરો."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"મોટું કરો"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 7a715c5..e803abe 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"कुछ ऐप्लिकेशन, पोर्ट्रेट मोड में सबसे अच्छी तरह काम करते हैं"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"जगह का पूरा इस्तेमाल करने के लिए, इनमें से किसी एक विकल्प को आज़माएं"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"फ़ुल स्क्रीन मोड में जाने के लिए, डिवाइस को घुमाएं"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"किसी ऐप्लिकेशन की जगह बदलने के लिए, उसके बगल में दो बार टैप करें"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ठीक है"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ज़्यादा जानकारी के लिए बड़ा करें."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"बड़ा करें"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index e21eab0..07b946d 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Neke aplikacije najbolje funkcioniraju u portretnom usmjerenju"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da biste maksimalno iskoristili prostor"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zakrenite uređaj radi prikaza na cijelom zaslonu"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da biste joj promijenili položaj"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Shvaćam"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite da biste saznali više."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziraj"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index eddb2fe5..08b35bf 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Egyes alkalmazások álló tájolásban működnek a leghatékonyabban"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Próbálja ki az alábbi beállítások egyikét, hogy a legjobban ki tudja használni képernyő területét"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"A teljes képernyős mód elindításához forgassa el az eszközt"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Koppintson duplán az alkalmazás mellett az áthelyezéséhez"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Értem"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kibontással további információkhoz juthat."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Teljes méret"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 877ee61..9d81e8c 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Որոշ հավելվածներ լավագույնս աշխատում են դիմանկարի ռեժիմում"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Փորձեք այս տարբերակներից մեկը՝ տարածքը հնարավորինս արդյունավետ օգտագործելու համար"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Պտտեք սարքը՝ լիաէկրան ռեժիմին անցնելու համար"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Կրկնակի հպեք հավելվածի կողքին՝ այն տեղափոխելու համար"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Եղավ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ծավալեք՝ ավելին իմանալու համար։"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Ծավալել"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 4137948..f74e83f 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Beberapa aplikasi berfungsi paling baik dalam mode potret"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Coba salah satu opsi berikut untuk mengoptimalkan area layar Anda"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Putar perangkat untuk tampilan layar penuh"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ketuk dua kali di samping aplikasi untuk mengubah posisinya"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Oke"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Luaskan untuk melihat informasi selengkapnya."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimalkan"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index c7847fc0..71f6ec7 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sum forrit virka best í skammsniði"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prófaðu einhvern af eftirfarandi valkostum til að nýta plássið sem best"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Snúðu tækinu til að nota allan skjáinn"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ýttu tvisvar við hlið forritsins til að færa það"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ég skil"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Stækka til að sjá frekari upplýsingar."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Stækka"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 66e5160..4583135 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Alcune app funzionano in modo ottimale in verticale"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prova una di queste opzioni per ottimizzare lo spazio a tua disposizione"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ruota il dispositivo per passare alla modalità a schermo intero"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tocca due volte accanto a un\'app per riposizionarla"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Espandi per avere ulteriori informazioni."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Ingrandisci"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index fa47100..bd52cd8 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"חלק מהאפליקציות פועלות בצורה הטובה ביותר במצב תצוגה לאורך"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"כדי להפיק את המרב משטח המסך, ניתן לנסות את אחת מהאפשרויות האלה"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"מסובבים את המכשיר כדי לעבור לתצוגה במסך מלא"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"מקישים הקשה כפולה ליד אפליקציה כדי למקם אותה מחדש"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"הבנתי"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"מרחיבים כדי לקבל מידע נוסף."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"הגדלה"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 84798d2..98b3ba6 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"アプリによっては縦向きにすると正常に動作します"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"スペースを最大限に活用するには、以下の方法のいずれかをお試しください"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"全画面表示にするにはデバイスを回転させてください"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"位置を変えるにはアプリの横をダブルタップしてください"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"開くと詳細が表示されます。"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 9a323c9..fbf0b5ec 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ზოგიერთი აპი უკეთ მუშაობს პორტრეტის რეჟიმში"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"გამოცადეთ ამ ვარიანტებიდან ერთ-ერთი, რათა მაქსიმალურად ისარგებლოთ თქვენი მეხსიერებით"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"მოატრიალეთ თქვენი მოწყობილობა სრული ეკრანის გასაშლელად"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ორმაგად შეეხეთ აპის გვერდითა სივრცეს, რათა ის სხვაგან გადაიტანოთ"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"გასაგებია"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"დამატებითი ინფორმაციისთვის გააფართოეთ."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"მაქსიმალურად გაშლა"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index f910693..a75dc3c 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Кейбір қолданба портреттік режимде жақсы жұмыс істейді"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Экранды тиімді пайдалану үшін мына опциялардың бірін байқап көріңіз."</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Толық экранға ауысу үшін құрылғыңызды бұрыңыз."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Қолданбаның орнын ауыстыру үшін жанынан екі рет түртіңіз."</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Түсінікті"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Толығырақ ақпарат алу үшін терезені жайыңыз."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Жаю"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 51f9c4f..6913c06 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហាពាក់ព័ន្ធនឹងកាមេរ៉ាឬ?\nចុចដើម្បីដោះស្រាយ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបានដោះស្រាយបញ្ហានេះទេឬ?\nចុចដើម្បីត្រឡប់"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមានបញ្ហាពាក់ព័ន្ធនឹងកាមេរ៉ាទេឬ? ចុចដើម្បីច្រានចោល។"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"កម្មវិធីមួយចំនួនដំណើរការបានប្រសើរបំផុតក្នុងទិសដៅបញ្ឈរ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"សាកល្បងជម្រើសមួយក្នុងចំណោមទាំងនេះ ដើម្បីទទួលបានអត្ថប្រយោជន៍ច្រើនបំផុតពីកន្លែងទំនេររបស់អ្នក"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"បង្វិលឧបករណ៍របស់អ្នក ដើម្បីចូលប្រើអេក្រង់ពេញ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ចុចពីរដងនៅជាប់កម្មវិធីណាមួយ ដើម្បីប្ដូរទីតាំងកម្មវិធីនោះ"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"យល់ហើយ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ពង្រីកដើម្បីទទួលបានព័ត៌មានបន្ថែម។"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ពង្រីក"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 36ba9f7..1f246d5 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ಕೆಲವು ಆ್ಯಪ್ಗಳು ಪೋರ್ಟ್ರೇಟ್ ಮೋಡ್ನಲ್ಲಿ ಅತ್ಯುತ್ತಮವಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತವೆ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ನಿಮ್ಮ ಸ್ಥಳಾವಕಾಶದ ಅತಿಹೆಚ್ಚು ಪ್ರಯೋಜನ ಪಡೆಯಲು ಈ ಆಯ್ಕೆಗಳಲ್ಲಿ ಒಂದನ್ನು ಬಳಸಿ ನೋಡಿ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ಗೆ ಹೋಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ಆ್ಯಪ್ ಒಂದರ ಸ್ಥಾನವನ್ನು ಬದಲಾಯಿಸಲು ಅದರ ಪಕ್ಕದಲ್ಲಿ ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ಸರಿ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ವಿಸ್ತೃತಗೊಳಿಸಿ."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ಹಿಗ್ಗಿಸಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 2c7c3fc..e878cef 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"일부 앱은 세로 모드에서 가장 잘 작동함"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"공간을 최대한 이용할 수 있도록 이 옵션 중 하나를 시도해 보세요."</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"전체 화면 모드로 전환하려면 기기를 회전하세요."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"앱 위치를 조정하려면 앱 옆을 두 번 탭하세요."</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"확인"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"추가 정보는 펼쳐서 확인하세요."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"최대화"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 5a487e6..20c462e 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Айрым колдонмолорду тигинен иштетүү туура болот"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Иш чөйрөсүнүн бардык мүмкүнчүлүктөрүн пайдалануу үчүн бул параметрлердин бирин колдонуп көрүңүз"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Толук экран режимине өтүү үчүн түзмөктү буруңуз"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Колдонмонун ракурсун өзгөртүү үчүн анын тушуна эки жолу басыңыз"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Түшүндүм"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Толук маалымат алуу үчүн жайып көрүңүз."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Чоңойтуу"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 558ca62..3f4a881 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອປິດໄວ້."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ແອັບບາງຢ່າງເຮັດວຽກໄດ້ດີທີ່ສຸດໃນໂໝດລວງຕັ້ງ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ໃຫ້ລອງຕົວເລືອກໃດໜຶ່ງເຫຼົ່ານີ້ເພື່ອໃຊ້ປະໂຫຍດຈາກພື້ນທີ່ຂອງທ່ານໃຫ້ໄດ້ສູງສຸດ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ໝຸນອຸປະກອນຂອງທ່ານເພື່ອໃຊ້ແບບເຕັມຈໍ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ແຕະສອງເທື່ອໃສ່ຖັດຈາກແອັບໃດໜຶ່ງເພື່ອຈັດຕຳແໜ່ງຂອງມັນຄືນໃໝ່"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ຂະຫຍາຍເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 88a6ac9..515a263 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Kai kurios programos geriausiai veikia stačiuoju režimu"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pabandykite naudoti vieną iš šių parinkčių, kad išnaudotumėte visą vietą"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pasukite įrenginį, kad įjungtumėte viso ekrano režimą"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dukart palieskite šalia programos, kad pakeistumėte jos poziciją"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Supratau"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Išskleiskite, jei reikia daugiau informacijos."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Padidinti"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index fbb3539..080c6f4 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Dažas lietotnes vislabāk darbojas portreta režīmā"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Izmēģiniet vienu no šīm iespējām, lai efektīvi izmantotu pieejamo vietu"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pagrieziet ierīci, lai aktivizētu pilnekrāna režīmu"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Veiciet dubultskārienu blakus lietotnei, lai manītu tās pozīciju"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Labi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Izvērsiet, lai iegūtu plašāku informāciju."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimizēt"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index e1ab00a8..47ed632 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Некои апликации најдобро работат во режим на портрет"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Испробајте една од опцииве за да го извлечете максимумот од вашиот простор"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ротирајте го уредот за да отворите на цел екран"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Допрете двапати до некоја апликација за да ја преместите"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Сфатив"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Проширете за повеќе информации."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Зголеми"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 1072dac..faa6a30 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ചില ആപ്പുകൾ പോർട്രെയ്റ്റിൽ മികച്ച രീതിയിൽ പ്രവർത്തിക്കുന്നു"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"നിങ്ങളുടെ ഇടം പരമാവധി പ്രയോജനപ്പെടുത്താൻ ഈ ഓപ്ഷനുകളിലൊന്ന് പരീക്ഷിക്കുക"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ഈ ഉപകരണം റൊട്ടേറ്റ് ചെയ്യുക"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ഒരു ആപ്പിന്റെ സ്ഥാനം മാറ്റാൻ, അതിന് തൊട്ടടുത്ത് ഡബിൾ ടാപ്പ് ചെയ്യുക"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"മനസ്സിലായി"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"കൂടുതൽ വിവരങ്ങൾക്ക് വികസിപ്പിക്കുക."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"വലുതാക്കുക"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 4210b8d..8e8d06d 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Зарим апп нь босоо чиглэлд хамгийн сайн ажилладаг"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Орон зайгаа сайтар ашиглахын тулд эдгээр сонголтуудын аль нэгийг туршиж үзээрэй"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Төхөөрөмжөө бүтэн дэлгэцээр үзэхийн тулд эргүүлнэ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Аппыг дахин байрлуулахын тулд хажууд нь хоёр товшино"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ойлголоо"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Нэмэлт мэдээлэл авах бол дэлгэнэ үү."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Томруулах"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 396e2129..4fc03b2 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्यासाठी टॅप करा."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"काही ॲप्स पोर्ट्रेटमध्ये सर्वोत्तम काम करतात"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"तुमच्या स्पेसचा पुरेपूर वापर करण्यासाठी, यांपैकी एक पर्याय वापरून पहा"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"फुल स्क्रीन करण्यासाठी, तुमचे डिव्हाइस फिरवा"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ॲपची स्थिती पुन्हा बदलण्यासाठी, त्याच्या शेजारी दोनदा टॅप करा"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"समजले"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"अधिक माहितीसाठी विस्तार करा."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"मोठे करा"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index f7a65bb..2db225a 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sesetengah apl berfungsi paling baik dalam mod potret"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Cuba salah satu daripada pilihan ini untuk memanfaatkan ruang anda sepenuhnya"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Putar peranti anda untuk beralih ke skrin penuh"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ketik dua kali bersebelahan apl untuk menempatkan semula apl"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kembangkan untuk mendapatkan maklumat lanjut."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimumkan"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 217c15b..e8ab4b0 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"အချို့အက်ပ်များသည် ဒေါင်လိုက်တွင် အကောင်းဆုံးလုပ်ဆောင်သည်"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"သင့်နေရာကို အကောင်းဆုံးအသုံးပြုနိုင်ရန် ဤရွေးစရာများထဲမှ တစ်ခုကို စမ်းကြည့်ပါ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ဖန်သားပြင်အပြည့်လုပ်ရန် သင့်စက်ကို လှည့်နိုင်သည်"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"အက်ပ်နေရာပြန်ချရန် ၎င်းဘေးတွင် နှစ်ချက်တို့နိုင်သည်"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ရပြီ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"နောက်ထပ်အချက်အလက်များအတွက် ချဲ့နိုင်သည်။"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ချဲ့ရန်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 6d5e481..278de2d 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Noen apper fungerer best i stående format"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prøv et av disse alternativene for å få mest mulig ut av plassen din"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Roter enheten for å starte fullskjerm"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dobbelttrykk ved siden av en app for å flytte den"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Greit"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vis for å få mer informasjon."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimer"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 6e84d8a..529f401 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"केही एपहरूले पोर्ट्रेटमा राम्रोसँग काम गर्छन्"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"तपाईं स्क्रिनको अधिकतम ठाउँ प्रयोग गर्न चाहनुहुन्छ भने यीमध्ये कुनै विकल्प प्रयोग गरी हेर्नुहोस्"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"तपाईं फुल स्क्रिन मोड हेर्न चाहनुहुन्छ भने आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"तपाईं जुन एपको स्थिति मिलाउन चाहनुहुन्छ सोही एपको छेउमा डबल ट्याप गर्नुहोस्"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"बुझेँ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"थप जानकारी प्राप्त गर्न चाहनुहुन्छ भने एक्स्पान्ड गर्नुहोस्।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ठुलो बनाउनुहोस्"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index e24ecb3..88c220c 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sommige apps werken het best in de staande stand"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Probeer een van deze opties om optimaal gebruik te maken van je ruimte"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Draai je apparaat om naar volledig scherm te schakelen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dubbeltik naast een app om deze opnieuw te positioneren"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Uitvouwen voor meer informatie."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximaliseren"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 74e9261..2c82fbc 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"କିଛି ଆପ ପୋର୍ଟ୍ରେଟରେ ସବୁଠାରୁ ଭଲ କାମ କରେ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ଆପଣଙ୍କ ସ୍ପେସରୁ ଅଧିକ ଲାଭ ପାଇବାକୁ ଏହି ବିକଳ୍ପଗୁଡ଼ିକ ମଧ୍ୟରୁ ଗୋଟିଏ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ପୂର୍ଣ୍ଣ-ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ରୋଟେଟ କରନ୍ତୁ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ଏକ ଆପକୁ ରିପୋଜିସନ କରିବା ପାଇଁ ଏହା ପାଖରେ ଦୁଇଥର-ଟାପ କରନ୍ତୁ"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ବୁଝିଗଲି"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ଅଧିକ ସୂଚନା ପାଇଁ ବିସ୍ତାର କରନ୍ତୁ।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ବଡ଼ କରନ୍ତୁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index cd5de57..8c7229f 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ਕੁਝ ਐਪਾਂ ਪੋਰਟਰੇਟ ਵਿੱਚ ਬਿਹਤਰ ਕੰਮ ਕਰਦੀਆਂ ਹਨ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ਆਪਣੀ ਜਗ੍ਹਾ ਦਾ ਵੱਧ ਤੋਂ ਵੱਧ ਲਾਹਾ ਲੈਣ ਲਈ ਇਨ੍ਹਾਂ ਵਿਕਲਪਾਂ ਵਿੱਚੋਂ ਕੋਈ ਇੱਕ ਵਰਤ ਕੇ ਦੇਖੋ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਣ ਲਈ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਘੁਮਾਓ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ਕਿਸੇ ਐਪ ਦੀ ਜਗ੍ਹਾ ਬਦਲਣ ਲਈ ਉਸ ਦੇ ਅੱਗੇ ਡਬਲ ਟੈਪ ਕਰੋ"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ਸਮਝ ਲਿਆ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਵਿਸਤਾਰ ਕਰੋ।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ਵੱਡਾ ਕਰੋ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 4ee0cb8..315b95e 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Niektóre aplikacje działają najlepiej w orientacji pionowej"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Wypróbuj jedną z tych opcji, aby jak najlepiej wykorzystać miejsce"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Obróć urządzenie, aby przejść do pełnego ekranu"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Kliknij dwukrotnie obok aplikacji, aby ją przenieść"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozwiń, aby wyświetlić więcej informacji."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksymalizuj"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 4f59329..48781ad 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Alguns apps funcionam melhor em modo retrato"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Tente uma destas opções para aproveitar seu espaço ao máximo"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gire o dispositivo para entrar no modo de tela cheia"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes ao lado de um app para reposicionar"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 37c5acf..e2be183 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algumas apps funcionam melhor no modo vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Experimente uma destas opções para aproveitar ao máximo o seu espaço"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rode o dispositivo para ficar em ecrã inteiro"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes junto a uma app para a reposicionar"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expandir para obter mais informações"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 4f59329..48781ad 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Alguns apps funcionam melhor em modo retrato"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Tente uma destas opções para aproveitar seu espaço ao máximo"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gire o dispositivo para entrar no modo de tela cheia"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes ao lado de um app para reposicionar"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 88b582e..65b0472 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Aveți probleme cu camera foto?\nAtingeți pentru a reîncadra"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ați remediat problema?\nAtingeți pentru a reveni"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu aveți probleme cu camera foto? Atingeți pentru a închide."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Unele aplicații funcționează cel mai bine în orientarea portret"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Încercați una dintre aceste opțiuni pentru a profita din plin de spațiu"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotiți dispozitivul pentru a trece în modul ecran complet"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Atingeți de două ori lângă o aplicație pentru a o repoziționa"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extindeți pentru mai multe informații"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizați"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 07d5dbb..8affb9a 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Некоторые приложения лучше работают в вертикальном режиме"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Чтобы эффективно использовать экранное пространство, выполните одно из следующих действий:"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Чтобы перейти в полноэкранный режим, поверните устройство."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Чтобы переместить приложение, нажмите на него дважды."</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ОК"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Развернуть, чтобы узнать больше."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Развернуть"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index dc23f81..c816065 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"සමහර යෙදුම් ප්රතිමූර්තිය තුළ හොඳින්ම ක්රියා කරයි"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ඔබගේ ඉඩෙන් උපරිම ප්රයෝජන ගැනීමට මෙම විකල්පවලින් එකක් උත්සාහ කරන්න"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"සම්පූර්ණ තිරයට යාමට ඔබගේ උපාංගය කරකවන්න"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"එය නැවත ස්ථානගත කිරීමට යෙදුමකට යාබදව දෙවරක් තට්ටු කරන්න"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"තේරුණා"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"වැඩිදුර තොරතුරු සඳහා දිග හරින්න"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"විහිදන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 53cb218..9dfbd54 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Niektoré aplikácie fungujú najlepšie v režime na výšku"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Vyskúšajte jednu z týchto možností a využívajte svoj priestor naplno"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Otočením zariadenia prejdete do režimu celej obrazovky"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvojitým klepnutím vedľa aplikácie zmeníte jej pozíciu"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Dobre"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Po rozbalení sa dozviete viac."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovať"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 27197bc..5bf943b 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Nekatere aplikacije najbolje delujejo v navpični postavitvi"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Poskusite eno od teh možnosti za čim boljši izkoristek prostora"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Če želite preklopiti v celozaslonski način, zasukajte napravo."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvakrat se dotaknite ob aplikaciji, če jo želite prestaviti."</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"V redu"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Razširitev za več informacij"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimiraj"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 64ef538..0955999 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Disa aplikacione funksionojnë më mirë në modalitetin vertikal"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Provo një nga këto opsione për ta shfrytëzuar sa më mirë hapësirën"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rrotullo ekranin për të kaluar në ekran të plotë"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Trokit dy herë pranë një aplikacioni për ta ripozicionuar"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"E kuptova"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Zgjeroje për më shumë informacion."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimizo"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index fe31879..b42d98e4 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Неке апликације најбоље функционишу у усправном режиму"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Испробајте једну од ових опција да бисте на најбољи начин искористили простор"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ротирајте уређај за приказ преко целог екрана"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Двапут додирните поред апликације да бисте променили њену позицију"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Важи"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Проширите за још информација."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Увећајте"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 2745838..162b57d 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Vissa appar fungerar bäst i stående läge"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Testa med ett av dessa alternativ för att få ut mest möjliga av ytan"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotera skärmen för att gå över till helskärmsläge"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tryck snabbt två gånger bredvid en app för att flytta den"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Utöka för mer information."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Utöka"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 565ecc4..3844d01 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Baadhi ya programu hufanya kazi vizuri zaidi zikiwa wima"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Jaribu moja kati ya chaguo hizi ili utumie nafasi ya skrini yako kwa ufanisi"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zungusha kifaa chako ili uende kwenye hali ya skrini nzima"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Gusa mara mbili karibu na programu ili uihamishe"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Nimeelewa"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Panua ili upate maelezo zaidi."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Panua"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index a5244d2..c45409c 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"சில ஆப்ஸ் \'போர்ட்ரெய்ட்டில்\' சிறப்பாகச் செயல்படும்"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ஸ்பேஸ்களிலிருந்து அதிகப் பலன்களைப் பெற இந்த விருப்பங்களில் ஒன்றைப் பயன்படுத்துங்கள்"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"முழுத்திரைக்குச் செல்ல உங்கள் சாதனத்தைச் சுழற்றவும்"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ஆப்ஸை இடம் மாற்ற, ஆப்ஸுக்கு அடுத்து இருமுறை தட்டவும்"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"சரி"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"கூடுதல் தகவல்களுக்கு விரிவாக்கலாம்."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"பெரிதாக்கும்"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index ee7c376..0a61f93 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"కొన్ని యాప్లు పోర్ట్రెయిట్లో ఉత్తమంగా పని చేస్తాయి"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"మీ ప్రదేశాన్ని ఎక్కువగా ఉపయోగించుకోవడానికి ఈ ఆప్షన్లలో ఒకదాన్ని ట్రై చేయండి"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ఫుల్ స్క్రీన్కు వెళ్లడానికి మీ పరికరాన్ని తిప్పండి"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"యాప్ స్థానాన్ని మార్చడానికి దాని పక్కన డబుల్-ట్యాప్ చేయండి"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"అర్థమైంది"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"మరింత సమాచారం కోసం విస్తరించండి."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"గరిష్టీకరించండి"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 6b1498b..0a41d45 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"บางแอปทำงานได้ดีที่สุดในแนวตั้ง"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ลองใช้หนึ่งในตัวเลือกเหล่านี้เพื่อให้ได้ประโยชน์สูงสุดจากพื้นที่ว่าง"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"หมุนอุปกรณ์ให้แสดงเต็มหน้าจอ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"แตะสองครั้งข้างแอปเพื่อเปลี่ยนตำแหน่ง"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"รับทราบ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ขยายเพื่อดูข้อมูลเพิ่มเติม"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ขยายใหญ่สุด"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index c6a3776..e272799 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"May ilang app na pinakamainam gamitin nang naka-portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Subukan ang isa sa mga opsyong ito para masulit ang iyong space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"I-rotate ang iyong device para mag-full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Mag-double tap sa tabi ng isang app para iposisyon ito ulit"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"I-expand para sa higit pang impormasyon."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"I-maximize"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 6014998..050fa5f 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Bazı uygulamalar dikey modda en iyi performansı gösterir"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Alanınızı en verimli şekilde kullanmak için bu seçeneklerden birini deneyin"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Tam ekrana geçmek için cihazınızı döndürün"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Yeniden konumlandırmak için uygulamanın yanına iki kez dokunun"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Daha fazla bilgi için genişletin."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Ekranı Kapla"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index a07d28a..d5f047f 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Деякі додатки найкраще працюють у вертикальній орієнтації"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Щоб максимально ефективно використовувати місце на екрані, спробуйте виконати одну з наведених нижче дій"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Щоб перейти в повноекранний режим, поверніть пристрій"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Щоб перемістити додаток, двічі торкніться області поруч із ним"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ОK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Розгорніть, щоб дізнатися більше."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Збільшити"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index d651cda..700ecaa 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"کچھ ایپس پورٹریٹ میں بہترین کام کرتی ہیں"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"اپنی اسپیس کا زیادہ سے زیادہ فائدہ اٹھانے کے لیے ان اختیارات میں سے ایک کو آزمائیں"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"پوری اسکرین پر جانے کیلئے اپنا آلہ گھمائیں"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"کسی ایپ کی پوزیشن تبدیل کرنے کے لیے اس کے آگے دو بار تھپتھپائیں"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"سمجھ آ گئی"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"مزید معلومات کے لیے پھیلائیں۔"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"بڑا کریں"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 9e1d871..e843b0b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Ayrim ilovalar tik holatda ishlashga eng mos"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Muhitdan yanada samarali foydalanish uchun quyidagilardan birini sinang"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Butun ekranda ochish uchun qurilmani buring"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Qayta joylash uchun keyingi ilova ustiga ikki marta bosing"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Batafsil axborot olish uchun kengaytiring."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Yoyish"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index f50e28b..ec1eadb 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Một số ứng dụng hoạt động tốt nhất ở chế độ dọc"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Hãy thử một trong các tuỳ chọn sau để tận dụng không gian"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Xoay thiết bị để chuyển sang chế độ toàn màn hình"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Nhấn đúp vào bên cạnh ứng dụng để đặt lại vị trí"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mở rộng để xem thêm thông tin."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Phóng to"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index a01acfd..37d4244 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"某些应用在纵向模式下才能发挥最佳效果"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"这些选项都有助于您最大限度地利用屏幕空间,不妨从中择一试试"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋转设备即可进入全屏模式"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在某个应用旁边连续点按两次,即可调整它的位置"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展开即可了解详情。"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 6d9a820..170cd4c 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"部分應用程式需要使用直向模式才能發揮最佳效果"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"請嘗試以下選項,充分運用螢幕的畫面空間"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋轉裝置方向即可進入全螢幕模式"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在應用程式旁輕按兩下即可調整位置"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展開即可查看詳情。"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 431ef18..83f8520 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"某些應用程式在直向模式下才能發揮最佳效果"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"請試試這裡的任一方式,以充分運用螢幕畫面的空間"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋轉裝置方向即可進入全螢幕模式"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在應用程式旁輕觸兩下即可調整位置"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"我知道了"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展開即可查看詳細資訊。"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 514b7b6..686310a 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -78,10 +78,12 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Amanye ama-app asebenza ngcono uma eme ngobude"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Zama enye yalezi zinketho ukuze usebenzise isikhala sakho ngokugcwele"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zungezisa idivayisi yakho ukuze uye esikrinini esigcwele"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Thepha kabili eduze kwe-app ukuze uyimise kabusha"</string>
+ <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
+ <skip />
+ <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
+ <skip />
+ <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
+ <skip />
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ngiyezwa"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Nweba ukuze uthole ulwazi olwengeziwe"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Khulisa"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index d2dd8d6b..5696b8d 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -81,6 +81,9 @@
<!-- The width and height of the background for custom action in PiP menu. -->
<dimen name="pip_custom_close_bg_size">32dp</dimen>
+ <!-- Extra padding between picture-in-picture windows and any registered keep clear areas. -->
+ <dimen name="pip_keep_clear_areas_padding">16dp</dimen>
+
<dimen name="dismiss_target_x_size">24dp</dimen>
<dimen name="floating_dismiss_bottom_margin">50dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
new file mode 100644
index 0000000..d276002
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import com.android.wm.shell.protolog.ShellProtoLogImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellInit;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Controls the {@link ShellProtoLogImpl} in WMShell via adb shell commands.
+ *
+ * Use with {@code adb shell dumpsys activity service SystemUIService WMShell protolog ...}.
+ */
+public class ProtoLogController implements ShellCommandHandler.ShellCommandActionHandler {
+ private final ShellCommandHandler mShellCommandHandler;
+ private final ShellProtoLogImpl mShellProtoLog;
+
+ public ProtoLogController(ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler) {
+ shellInit.addInitCallback(this::onInit, this);
+ mShellCommandHandler = shellCommandHandler;
+ mShellProtoLog = ShellProtoLogImpl.getSingleInstance();
+ }
+
+ void onInit() {
+ mShellCommandHandler.addCommandCallback("protolog", this, this);
+ }
+
+ @Override
+ public boolean onShellCommand(String[] args, PrintWriter pw) {
+ switch (args[0]) {
+ case "status": {
+ pw.println(mShellProtoLog.getStatus());
+ return true;
+ }
+ case "start": {
+ mShellProtoLog.startProtoLog(pw);
+ return true;
+ }
+ case "stop": {
+ mShellProtoLog.stopProtoLog(pw, true /* writeToFile */);
+ return true;
+ }
+ case "enable-text": {
+ String[] groups = Arrays.copyOfRange(args, 1, args.length);
+ int result = mShellProtoLog.startTextLogging(groups, pw);
+ if (result == 0) {
+ pw.println("Starting logging on groups: " + Arrays.toString(groups));
+ return true;
+ }
+ return false;
+ }
+ case "disable-text": {
+ String[] groups = Arrays.copyOfRange(args, 1, args.length);
+ int result = mShellProtoLog.stopTextLogging(groups, pw);
+ if (result == 0) {
+ pw.println("Stopping logging on groups: " + Arrays.toString(groups));
+ return true;
+ }
+ return false;
+ }
+ case "enable": {
+ String[] groups = Arrays.copyOfRange(args, 1, args.length);
+ return mShellProtoLog.startTextLogging(groups, pw) == 0;
+ }
+ case "disable": {
+ String[] groups = Arrays.copyOfRange(args, 1, args.length);
+ return mShellProtoLog.stopTextLogging(groups, pw) == 0;
+ }
+ default: {
+ pw.println("Invalid command: " + args[0]);
+ printShellCommandHelp(pw, "");
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public void printShellCommandHelp(PrintWriter pw, String prefix) {
+ pw.println(prefix + "status");
+ pw.println(prefix + " Get current ProtoLog status.");
+ pw.println(prefix + "start");
+ pw.println(prefix + " Start proto logging.");
+ pw.println(prefix + "stop");
+ pw.println(prefix + " Stop proto logging and flush to file.");
+ pw.println(prefix + "enable [group...]");
+ pw.println(prefix + " Enable proto logging for given groups.");
+ pw.println(prefix + "disable [group...]");
+ pw.println(prefix + " Disable proto logging for given groups.");
+ pw.println(prefix + "enable-text [group...]");
+ pw.println(prefix + " Enable logcat logging for given groups.");
+ pw.println(prefix + "disable-text [group...]");
+ pw.println(prefix + " Disable logcat logging for given groups.");
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index d5d4935..4367936 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -16,6 +16,7 @@
package com.android.wm.shell;
+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;
@@ -695,15 +696,19 @@
/**
* Create a {@link WindowContainerTransaction} to clear task bounds.
*
+ * Only affects tasks that have {@link RunningTaskInfo#getActivityType()} set to
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
+ *
* @param displayId display id for tasks that will have bounds cleared
* @return {@link WindowContainerTransaction} with pending operations to clear bounds
*/
- public WindowContainerTransaction prepareClearBoundsForTasks(int displayId) {
+ public WindowContainerTransaction prepareClearBoundsForStandardTasks(int displayId) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks: displayId=%d", displayId);
WindowContainerTransaction wct = new WindowContainerTransaction();
for (int i = 0; i < mTasks.size(); i++) {
RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo();
- if (taskInfo.displayId == displayId) {
+ if ((taskInfo.displayId == displayId) && (taskInfo.getActivityType()
+ == WindowConfiguration.ACTIVITY_TYPE_STANDARD)) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s",
taskInfo.token, taskInfo);
wct.setBounds(taskInfo.token, null);
@@ -715,17 +720,21 @@
/**
* Create a {@link WindowContainerTransaction} to clear task level freeform setting.
*
+ * Only affects tasks that have {@link RunningTaskInfo#getActivityType()} set to
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
+ *
* @param displayId display id for tasks that will have windowing mode reset to {@link
* WindowConfiguration#WINDOWING_MODE_UNDEFINED}
* @return {@link WindowContainerTransaction} with pending operations to clear windowing mode
*/
- public WindowContainerTransaction prepareClearFreeformForTasks(int displayId) {
+ public WindowContainerTransaction prepareClearFreeformForStandardTasks(int displayId) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks: displayId=%d", displayId);
WindowContainerTransaction wct = new WindowContainerTransaction();
for (int i = 0; i < mTasks.size(); i++) {
RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo();
if (taskInfo.displayId == displayId
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE,
"clearing windowing mode for token=%s taskInfo=%s", taskInfo.token,
taskInfo);
@@ -867,8 +876,12 @@
pkg = info.getTaskInfo().baseActivity.getPackageName();
}
Rect bounds = info.getTaskInfo().getConfiguration().windowConfiguration.getBounds();
+ boolean running = info.getTaskInfo().isRunning;
+ boolean visible = info.getTaskInfo().isVisible;
+ boolean focused = info.getTaskInfo().isFocused;
pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener
- + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds);
+ + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds
+ + " running=" + running + " visible=" + visible + " focused=" + focused);
}
pw.println();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING b/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING
new file mode 100644
index 0000000..8dd1369
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "ironwood-postsubmit": [
+ {
+ "name": "WMShellFlickerTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.IwTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
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 99b8885..b5a5754 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
@@ -170,7 +170,8 @@
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
- int taskId, @Nullable final String locus, Executor mainExecutor) {
+ int taskId, @Nullable final String locus, Executor mainExecutor,
+ final Bubbles.BubbleMetadataFlagListener listener) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mMetadataShortcutId = shortcutInfo.getId();
@@ -188,11 +189,12 @@
mShowBubbleUpdateDot = false;
mMainExecutor = mainExecutor;
mTaskId = taskId;
+ mBubbleMetadataFlagListener = listener;
}
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final BubbleEntry entry,
- @Nullable final Bubbles.BubbleMetadataFlagListener listener,
+ final Bubbles.BubbleMetadataFlagListener listener,
final Bubbles.PendingIntentCanceledListener intentCancelListener,
Executor mainExecutor) {
mKey = entry.getKey();
@@ -830,6 +832,7 @@
pw.print(" desiredHeight: "); pw.println(getDesiredHeightString());
pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
+ pw.print(" bubbleMetadataFlagListener null: " + (mBubbleMetadataFlagListener == null));
if (mExpandedView != null) {
mExpandedView.dump(pw);
}
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 d63c25d..0e8dd4d 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
@@ -309,6 +309,7 @@
protected void onInit() {
mBubbleData.setListener(mBubbleDataListener);
mBubbleData.setSuppressionChangedListener(this::onBubbleMetadataFlagChanged);
+ mDataRepository.setSuppressionChangedListener(this::onBubbleMetadataFlagChanged);
mBubbleData.setPendingIntentCancelledListener(bubble -> {
if (bubble.getBubbleIntent() == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index c64133f..af31391 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -158,7 +158,6 @@
@Nullable
private Listener mListener;
- @Nullable
private Bubbles.BubbleMetadataFlagListener mBubbleMetadataFlagListener;
private Bubbles.PendingIntentCanceledListener mCancelledListener;
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 97560f4..3a59614 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
@@ -25,6 +25,7 @@
import android.content.pm.UserInfo
import android.os.UserHandle
import android.util.Log
+import com.android.wm.shell.bubbles.Bubbles.BubbleMetadataFlagListener
import com.android.wm.shell.bubbles.storage.BubbleEntity
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository
@@ -47,6 +48,13 @@
private val ioScope = CoroutineScope(Dispatchers.IO)
private var job: Job? = null
+ // For use in Bubble construction.
+ private lateinit var bubbleMetadataFlagListener: BubbleMetadataFlagListener
+
+ fun setSuppressionChangedListener(listener: BubbleMetadataFlagListener) {
+ bubbleMetadataFlagListener = listener
+ }
+
/**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
@@ -197,7 +205,8 @@
entity.title,
entity.taskId,
entity.locus,
- mainExecutor
+ mainExecutor,
+ bubbleMetadataFlagListener
)
}
}
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 15bfeb2..e9957fd 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
@@ -16,16 +16,32 @@
package com.android.wm.shell.dagger;
-import android.view.IWindowManager;
+import android.content.Context;
+import android.os.Handler;
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.splitscreen.tv.TvSplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.tv.TvStartingWindowTypeAlgorithm;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.Optional;
import dagger.Module;
import dagger.Provides;
@@ -50,5 +66,33 @@
@DynamicOverride
static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
return new TvStartingWindowTypeAlgorithm();
- };
+ }
+
+ @WMSingleton
+ @Provides
+ @DynamicOverride
+ static SplitScreenController provideSplitScreenController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
+ DragAndDropController dragAndDropController,
+ Transitions transitions,
+ TransactionPool transactionPool,
+ IconProvider iconProvider,
+ Optional<RecentTasksController> recentTasks,
+ @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);
+ }
}
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 7a736cc..c39602032 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
@@ -27,6 +27,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.ProtoLogController;
import com.android.wm.shell.RootDisplayAreaOrganizer;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -699,6 +700,7 @@
Optional<ActivityEmbeddingController> activityEmbeddingOptional,
Transitions transitions,
StartingWindowController startingWindow,
+ ProtoLogController protoLogController,
@ShellCreateTriggerOverride Optional<Object> overriddenCreateTrigger) {
return new Object();
}
@@ -714,4 +716,12 @@
static ShellCommandHandler provideShellCommandHandler() {
return new ShellCommandHandler();
}
+
+ @WMSingleton
+ @Provides
+ static ProtoLogController provideProtoLogController(
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler) {
+ return new ProtoLogController(shellInit, shellCommandHandler);
+ }
}
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 31596f3..7c50982 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
@@ -49,7 +49,7 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.desktopmode.DesktopModeConstants;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
@@ -72,9 +72,9 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipController;
-import com.android.wm.shell.pip.phone.PipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
@@ -220,13 +220,14 @@
Context context,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
+ Optional<RecentTasksController> recentTasksController,
WindowDecorViewModel<?> windowDecorViewModel) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
ShellInit init = FreeformComponents.isFreeformEnabled(context)
? shellInit
: null;
- return new FreeformTaskListener<>(init, shellTaskOrganizer,
+ return new FreeformTaskListener<>(init, shellTaskOrganizer, recentTasksController,
windowDecorViewModel);
}
@@ -320,7 +321,7 @@
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
- PipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
PipBoundsState pipBoundsState,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
@@ -357,15 +358,17 @@
@WMSingleton
@Provides
- static PipKeepClearAlgorithm providePipKeepClearAlgorithm() {
- return new PipKeepClearAlgorithm();
+ static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) {
+ return new PhonePipKeepClearAlgorithm(context);
}
@WMSingleton
@Provides
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
- PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
- return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
+ PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
+ PhonePipKeepClearAlgorithm pipKeepClearAlgorithm) {
+ return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
+ pipKeepClearAlgorithm);
}
// Handler is used by Icon.loadDrawableAsync
@@ -597,7 +600,7 @@
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
@ShellMainThread Handler mainHandler
) {
- if (DesktopModeConstants.IS_FEATURE_ENABLED) {
+ if (DesktopMode.IS_SUPPORTED) {
return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer,
rootDisplayAreaOrganizer,
mainHandler));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
new file mode 100644
index 0000000..8993d54
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+/**
+ * Constants for desktop mode feature
+ */
+public class DesktopMode {
+
+ /**
+ * Flag to indicate whether desktop mode is available on the device
+ */
+ public static final boolean IS_SUPPORTED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode", false);
+
+ /**
+ * Check if desktop mode is active
+ *
+ * @return {@code true} if active
+ */
+ public static boolean isActive(Context context) {
+ if (!IS_SUPPORTED) {
+ return false;
+ }
+ try {
+ int result = Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
+ return result != 0;
+ } catch (Exception e) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
+ return false;
+ }
+ }
+}
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 5849e16..7d34ea4 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
@@ -62,25 +62,29 @@
private void onInit() {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController");
mSettingsObserver.observe();
+ if (DesktopMode.isActive(mContext)) {
+ updateDesktopModeActive(true);
+ }
}
@VisibleForTesting
- void updateDesktopModeEnabled(boolean enabled) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeState: enabled=%s", enabled);
+ void updateDesktopModeActive(boolean active) {
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active);
int displayId = mContext.getDisplayId();
WindowContainerTransaction wct = new WindowContainerTransaction();
// Reset freeform windowing mode that is set per task level (tasks should inherit
// container value)
- wct.merge(mShellTaskOrganizer.prepareClearFreeformForTasks(displayId), true /* transfer */);
+ wct.merge(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(displayId),
+ true /* transfer */);
int targetWindowingMode;
- if (enabled) {
+ if (active) {
targetWindowingMode = WINDOWING_MODE_FREEFORM;
} else {
targetWindowingMode = WINDOWING_MODE_FULLSCREEN;
// Clear any resized bounds
- wct.merge(mShellTaskOrganizer.prepareClearBoundsForTasks(displayId),
+ wct.merge(mShellTaskOrganizer.prepareClearBoundsForStandardTasks(displayId),
true /* transfer */);
}
wct.merge(mRootDisplayAreaOrganizer.prepareWindowingModeChange(displayId,
@@ -118,20 +122,8 @@
}
private void desktopModeSettingChanged() {
- boolean enabled = isDesktopModeEnabled();
- updateDesktopModeEnabled(enabled);
- }
-
- private boolean isDesktopModeEnabled() {
- try {
- int result = Settings.System.getIntForUser(mContext.getContentResolver(),
- Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
- return result != 0;
- } catch (Settings.SettingNotFoundException e) {
- ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
- return false;
- }
+ boolean enabled = DesktopMode.isActive(mContext);
+ updateDesktopModeActive(enabled);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 8dcdda1..1baac71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -28,12 +28,15 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.recents.RecentTasksController;
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.util.Optional;
/**
* {@link ShellTaskOrganizer.TaskListener} for {@link
@@ -46,6 +49,7 @@
private static final String TAG = "FreeformTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer;
+ private final Optional<RecentTasksController> mRecentTasksOptional;
private final WindowDecorViewModel<T> mWindowDecorationViewModel;
private final SparseArray<State<T>> mTasks = new SparseArray<>();
@@ -60,9 +64,11 @@
public FreeformTaskListener(
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
+ Optional<RecentTasksController> recentTasksController,
WindowDecorViewModel<T> windowDecorationViewModel) {
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
+ mRecentTasksOptional = recentTasksController;
if (shellInit != null) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -83,6 +89,12 @@
mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
t.apply();
}
+
+ if (DesktopMode.IS_SUPPORTED && taskInfo.isVisible) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Adding active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ }
}
private State<T> createOrUpdateTaskState(RunningTaskInfo taskInfo, SurfaceControl leash) {
@@ -111,6 +123,12 @@
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
+ if (DesktopMode.IS_SUPPORTED) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Removing active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.removeActiveFreeformTask(taskInfo.taskId));
+ }
+
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
// Save window decorations of closing tasks so that we can hand them over to the
// transition system if this method happens before the transition. In case where the
@@ -131,6 +149,14 @@
if (state.mWindowDecoration != null) {
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
}
+
+ if (DesktopMode.IS_SUPPORTED) {
+ if (taskInfo.isVisible) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Adding active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ }
+ }
}
private State<T> updateTaskInfo(RunningTaskInfo taskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index e03421d..4def15d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -37,12 +37,13 @@
* @param activityInfo ActivityInfo tied to the Activity
* @param pictureInPictureParams PictureInPictureParams tied to the Activity
* @param launcherRotation Launcher rotation to calculate the PiP destination bounds
- * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds
+ * @param hotseatKeepClearArea Bounds of Hotseat to avoid used to calculate PiP destination
+ bounds
* @return destination bounds the PiP window should land into
*/
Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
in PictureInPictureParams pictureInPictureParams,
- int launcherRotation, int shelfHeight) = 1;
+ int launcherRotation, in Rect hotseatKeepClearArea) = 1;
/**
* Notifies the swiping Activity to PiP onto home transition is finished
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index 7397e52..cd61dbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -47,6 +47,7 @@
private final @NonNull PipBoundsState mPipBoundsState;
private final PipSnapAlgorithm mSnapAlgorithm;
+ private final PipKeepClearAlgorithm mPipKeepClearAlgorithm;
private float mDefaultSizePercent;
private float mMinAspectRatioForMinSize;
@@ -60,9 +61,11 @@
protected Point mScreenEdgeInsets;
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState,
- @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
+ @NonNull PipSnapAlgorithm pipSnapAlgorithm,
+ @NonNull PipKeepClearAlgorithm pipKeepClearAlgorithm) {
mPipBoundsState = pipBoundsState;
mSnapAlgorithm = pipSnapAlgorithm;
+ mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
reloadResources(context);
// Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
// resources as it would clobber mAspectRatio when entering PiP from fullscreen which
@@ -129,8 +132,21 @@
return getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */);
}
- /** Returns the destination bounds to place the PIP window on entry. */
+ /**
+ * Returns the destination bounds to place the PIP window on entry.
+ * If there are any keep clear areas registered, the position will try to avoid occluding them.
+ */
public Rect getEntryDestinationBounds() {
+ Rect entryBounds = getEntryDestinationBoundsIgnoringKeepClearAreas();
+ Rect insets = new Rect();
+ getInsetBounds(insets);
+ return mPipKeepClearAlgorithm.findUnoccludedPosition(entryBounds,
+ mPipBoundsState.getRestrictedKeepClearAreas(),
+ mPipBoundsState.getUnrestrictedKeepClearAreas(), insets);
+ }
+
+ /** Returns the destination bounds to place the PIP window on entry. */
+ public Rect getEntryDestinationBoundsIgnoringKeepClearAreas() {
final PipBoundsState.PipReentryState reentryState = mPipBoundsState.getReentryState();
final Rect destinationBounds = reentryState != null
@@ -138,9 +154,10 @@
: getDefaultBounds();
final boolean useCurrentSize = reentryState != null && reentryState.getSize() != null;
- return transformBoundsToAspectRatioIfValid(destinationBounds,
+ Rect aspectRatioBounds = transformBoundsToAspectRatioIfValid(destinationBounds,
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
useCurrentSize);
+ return aspectRatioBounds;
}
/** Returns the current bounds adjusted to the new aspect ratio, if valid. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java
new file mode 100644
index 0000000..e3495e1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import android.graphics.Rect;
+
+import java.util.Set;
+
+/**
+ * Interface for interacting with keep clear algorithm used to move PiP window out of the way of
+ * keep clear areas.
+ */
+public interface PipKeepClearAlgorithm {
+
+ /**
+ * Adjust the position of picture in picture window based on the registered keep clear areas.
+ * @param pipBoundsState state of the PiP to use for the calculations
+ * @param pipBoundsAlgorithm algorithm implementation used to get the entry destination bounds
+ * @return
+ */
+ default Rect adjust(PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm) {
+ return pipBoundsState.getBounds();
+ }
+
+ /**
+ * Calculate the bounds so that none of the keep clear areas are occluded, while the bounds stay
+ * within the allowed bounds. If such position is not feasible, return original bounds.
+ * @param defaultBounds initial bounds used in the calculation
+ * @param restrictedKeepClearAreas registered restricted keep clear areas
+ * @param unrestrictedKeepClearAreas registered unrestricted keep clear areas
+ * @param allowedBounds bounds that define the allowed space for the output, result will always
+ * be inside those bounds
+ * @return bounds that don't cover any of the keep clear areas and are within allowed bounds
+ */
+ default Rect findUnoccludedPosition(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas,
+ Set<Rect> unrestrictedKeepClearAreas, Rect allowedBounds) {
+ return defaultBounds;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b46eff6..297c79e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1306,6 +1306,12 @@
return;
}
+ if (mLeash == null || !mLeash.isValid()) {
+ Log.e(TAG, String.format("scheduleFinishResizePip with null leash! mState=%d",
+ mPipTransitionState.getTransitionState()));
+ return;
+ }
+
finishResize(createFinishResizeSurfaceTransaction(destinationBounds), destinationBounds,
direction, -1);
if (updateBoundsCallback != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
index 1a4be3b..c6b5ce9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
@@ -88,6 +88,11 @@
return isInPip(mState);
}
+ /** Returns true if activity has fully entered PiP mode. */
+ public boolean hasEnteredPip() {
+ return hasEnteredPip(mState);
+ }
+
public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) {
mInSwipePipToHomeTransition = inSwipePipToHomeTransition;
}
@@ -120,6 +125,11 @@
return state >= TASK_APPEARED && state != EXITING_PIP;
}
+ /** Returns true if activity has fully entered PiP mode. */
+ public static boolean hasEnteredPip(@TransitionState int state) {
+ return state == ENTERED_PIP;
+ }
+
public interface OnPipTransitionStateChangedListener {
void onPipTransitionStateChanged(@TransitionState int oldState,
@TransitionState int newState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
new file mode 100644
index 0000000..6dd02e4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.ArraySet;
+import android.view.Gravity;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipKeepClearAlgorithm;
+
+import java.util.Set;
+
+/**
+ * Calculates the adjusted position that does not occlude keep clear areas.
+ */
+public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
+
+ protected int mKeepClearAreasPadding;
+
+ public PhonePipKeepClearAlgorithm(Context context) {
+ reloadResources(context);
+ }
+
+ private void reloadResources(Context context) {
+ final Resources res = context.getResources();
+ mKeepClearAreasPadding = res.getDimensionPixelSize(R.dimen.pip_keep_clear_areas_padding);
+ }
+
+ /**
+ * Adjusts the current position of PiP to avoid occluding keep clear areas. This will push PiP
+ * towards the closest edge and then apply calculations to avoid occluding keep clear areas.
+ */
+ public Rect adjust(PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm) {
+ Rect startingBounds = pipBoundsState.getBounds().isEmpty()
+ ? pipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas()
+ : pipBoundsState.getBounds();
+ float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
+ int verticalGravity;
+ int horizontalGravity;
+ if (snapFraction < 1.5f || snapFraction >= 3.5f) {
+ verticalGravity = Gravity.NO_GRAVITY;
+ } else {
+ verticalGravity = Gravity.BOTTOM;
+ }
+ if (snapFraction >= 0.5f && snapFraction < 2.5f) {
+ horizontalGravity = Gravity.RIGHT;
+ } else {
+ horizontalGravity = Gravity.LEFT;
+ }
+ // push the bounds based on the gravity
+ Rect insets = new Rect();
+ pipBoundsAlgorithm.getInsetBounds(insets);
+ if (pipBoundsState.isImeShowing()) {
+ insets.bottom -= pipBoundsState.getImeHeight();
+ }
+ Rect pushedBounds = new Rect(startingBounds);
+ if (verticalGravity == Gravity.BOTTOM) {
+ pushedBounds.offsetTo(pushedBounds.left,
+ insets.bottom - pushedBounds.height());
+ }
+ if (horizontalGravity == Gravity.RIGHT) {
+ pushedBounds.offsetTo(insets.right - pushedBounds.width(), pushedBounds.top);
+ } else {
+ pushedBounds.offsetTo(insets.left, pushedBounds.top);
+ }
+ return findUnoccludedPosition(pushedBounds, pipBoundsState.getRestrictedKeepClearAreas(),
+ pipBoundsState.getUnrestrictedKeepClearAreas(), insets);
+ }
+
+ /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */
+ public Rect findUnoccludedPosition(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas,
+ Set<Rect> unrestrictedKeepClearAreas, Rect allowedBounds) {
+ if (restrictedKeepClearAreas.isEmpty() && unrestrictedKeepClearAreas.isEmpty()) {
+ return defaultBounds;
+ }
+ Set<Rect> keepClearAreas = new ArraySet<>();
+ if (!restrictedKeepClearAreas.isEmpty()) {
+ keepClearAreas.addAll(restrictedKeepClearAreas);
+ }
+ if (!unrestrictedKeepClearAreas.isEmpty()) {
+ keepClearAreas.addAll(unrestrictedKeepClearAreas);
+ }
+ Rect outBounds = new Rect(defaultBounds);
+ for (Rect r : keepClearAreas) {
+ Rect tmpRect = new Rect(r);
+ // add extra padding to the keep clear area
+ tmpRect.inset(-mKeepClearAreasPadding, -mKeepClearAreasPadding);
+ if (Rect.intersects(r, outBounds)) {
+ if (tryOffsetUp(outBounds, tmpRect, allowedBounds)) continue;
+ if (tryOffsetLeft(outBounds, tmpRect, allowedBounds)) continue;
+ if (tryOffsetDown(outBounds, tmpRect, allowedBounds)) continue;
+ if (tryOffsetRight(outBounds, tmpRect, allowedBounds)) continue;
+ }
+ }
+ return outBounds;
+ }
+
+ private static boolean tryOffsetLeft(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds) {
+ return tryOffset(rectToMove, rectToAvoid, allowedBounds,
+ rectToAvoid.left - rectToMove.right, 0);
+ }
+
+ private static boolean tryOffsetRight(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds) {
+ return tryOffset(rectToMove, rectToAvoid, allowedBounds,
+ rectToAvoid.right - rectToMove.left, 0);
+ }
+
+ private static boolean tryOffsetUp(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds) {
+ return tryOffset(rectToMove, rectToAvoid, allowedBounds,
+ 0, rectToAvoid.top - rectToMove.bottom);
+ }
+
+ private static boolean tryOffsetDown(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds) {
+ return tryOffset(rectToMove, rectToAvoid, allowedBounds,
+ 0, rectToAvoid.bottom - rectToMove.top);
+ }
+
+ private static boolean tryOffset(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds,
+ int dx, int dy) {
+ Rect tmp = new Rect(rectToMove);
+ tmp.offset(dx, dy);
+ if (!Rect.intersects(rectToAvoid, tmp) && allowedBounds.contains(tmp)) {
+ rectToMove.offsetTo(tmp.left, tmp.top);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index ac3407d..6c9a6b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -43,6 +43,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Pair;
@@ -79,6 +80,7 @@
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipKeepClearAlgorithm;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -110,6 +112,14 @@
UserChangeListener {
private static final String TAG = "PipController";
+ private boolean mEnablePipKeepClearAlgorithm =
+ SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", false);
+
+ @VisibleForTesting
+ void setEnablePipKeepClearAlgorithm(boolean value) {
+ mEnablePipKeepClearAlgorithm = value;
+ }
+
private Context mContext;
protected ShellExecutor mMainExecutor;
private DisplayController mDisplayController;
@@ -262,7 +272,17 @@
public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
Set<Rect> unrestricted) {
if (mPipBoundsState.getDisplayId() == displayId) {
- mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
+ if (mEnablePipKeepClearAlgorithm) {
+ mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
+ // only move if already in pip, other transitions account for keep clear
+ // areas
+ if (mPipTransitionState.hasEnteredPip()) {
+ Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState,
+ mPipBoundsAlgorithm);
+ mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
+ mEnterAnimationDuration, null);
+ }
+ }
}
}
};
@@ -759,8 +779,16 @@
private Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams,
- int launcherRotation, int shelfHeight) {
- setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
+ int launcherRotation, Rect hotseatKeepClearArea) {
+
+ if (mEnablePipKeepClearAlgorithm) {
+ // pre-emptively add the keep clear area for Hotseat, so that it is taken into account
+ // when calculating the entry destination bounds of PiP window
+ mPipBoundsState.getRestrictedKeepClearAreas().add(hotseatKeepClearArea);
+ } else {
+ int shelfHeight = hotseatKeepClearArea.height();
+ setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
+ }
onDisplayRotationChangedNotInPip(mContext, launcherRotation);
final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams);
@@ -1059,12 +1087,12 @@
@Override
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams, int launcherRotation,
- int shelfHeight) {
+ Rect keepClearArea) {
Rect[] result = new Rect[1];
executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
(controller) -> {
result[0] = controller.startSwipePipToHome(componentName, activityInfo,
- pictureInPictureParams, launcherRotation, shelfHeight);
+ pictureInPictureParams, launcherRotation, keepClearArea);
}, true /* blocking */);
return result[0];
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java
deleted file mode 100644
index 78084fa..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.pip.phone;
-
-import android.graphics.Rect;
-import android.util.ArraySet;
-
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-
-import java.util.Set;
-
-/**
- * Calculates the adjusted position that does not occlude keep clear areas.
- */
-public class PipKeepClearAlgorithm {
-
- /**
- * Adjusts the current position of PiP to avoid occluding keep clear areas. If the user has
- * moved PiP manually, the unmodified current position will be returned instead.
- */
- public Rect adjust(PipBoundsState boundsState, PipBoundsAlgorithm boundsAlgorithm) {
- if (boundsState.hasUserResizedPip()) {
- return boundsState.getBounds();
- }
- return adjust(boundsAlgorithm.getEntryDestinationBounds(),
- boundsState.getRestrictedKeepClearAreas(),
- boundsState.getUnrestrictedKeepClearAreas(), boundsState.getDisplayBounds());
- }
-
- /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */
- public Rect adjust(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas,
- Set<Rect> unrestrictedKeepClearAreas, Rect displayBounds) {
- if (restrictedKeepClearAreas.isEmpty()) {
- return defaultBounds;
- }
- Set<Rect> keepClearAreas = new ArraySet<>();
- if (!restrictedKeepClearAreas.isEmpty()) {
- keepClearAreas.addAll(restrictedKeepClearAreas);
- }
- Rect outBounds = new Rect(defaultBounds);
- for (Rect r : keepClearAreas) {
- if (Rect.intersects(r, outBounds)) {
- if (tryOffsetUp(outBounds, r, displayBounds)) continue;
- if (tryOffsetLeft(outBounds, r, displayBounds)) continue;
- if (tryOffsetDown(outBounds, r, displayBounds)) continue;
- if (tryOffsetRight(outBounds, r, displayBounds)) continue;
- }
- }
- return outBounds;
- }
-
- private boolean tryOffsetLeft(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) {
- return tryOffset(rectToMove, rectToAvoid, displayBounds,
- rectToAvoid.left - rectToMove.right, 0);
- }
-
- private boolean tryOffsetRight(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) {
- return tryOffset(rectToMove, rectToAvoid, displayBounds,
- rectToAvoid.right - rectToMove.left, 0);
- }
-
- private boolean tryOffsetUp(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) {
- return tryOffset(rectToMove, rectToAvoid, displayBounds,
- 0, rectToAvoid.top - rectToMove.bottom);
- }
-
- private boolean tryOffsetDown(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) {
- return tryOffset(rectToMove, rectToAvoid, displayBounds,
- 0, rectToAvoid.bottom - rectToMove.top);
- }
-
- private boolean tryOffset(Rect rectToMove, Rect rectToAvoid, Rect displayBounds,
- int dx, int dy) {
- Rect tmp = new Rect(rectToMove);
- tmp.offset(dx, dy);
- if (!Rect.intersects(rectToAvoid, tmp) && displayBounds.contains(tmp)) {
- rectToMove.offsetTo(tmp.left, tmp.top);
- return true;
- }
- return false;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 1958157..979b7c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -207,6 +207,9 @@
}
});
+ // this disables the ripples
+ mEnterSplitButton.setEnabled(false);
+
findViewById(R.id.resize_handle).setAlpha(0);
mActionsGroup = findViewById(R.id.actions_group);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
index 85441af..5aa3c4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
@@ -1,2 +1,3 @@
# WM shell sub-module TV pip owner
galinap@google.com
+bronger@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index 815f3dd..a4b7033 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -38,6 +38,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipKeepClearAlgorithm;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -48,11 +49,10 @@
* Contains pip bounds calculations that are specific to TV.
*/
public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
-
private static final String TAG = TvPipBoundsAlgorithm.class.getSimpleName();
- private static final boolean DEBUG = TvPipController.DEBUG;
- private final @NonNull TvPipBoundsState mTvPipBoundsState;
+ @NonNull
+ private final TvPipBoundsState mTvPipBoundsState;
private int mFixedExpandedHeightInPx;
private int mFixedExpandedWidthInPx;
@@ -62,7 +62,8 @@
public TvPipBoundsAlgorithm(Context context,
@NonNull TvPipBoundsState tvPipBoundsState,
@NonNull PipSnapAlgorithm pipSnapAlgorithm) {
- super(context, tvPipBoundsState, pipSnapAlgorithm);
+ super(context, tvPipBoundsState, pipSnapAlgorithm,
+ new PipKeepClearAlgorithm() {});
this.mTvPipBoundsState = tvPipBoundsState;
this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
reloadResources(context);
@@ -89,10 +90,9 @@
/** Returns the destination bounds to place the PIP window on entry. */
@Override
public Rect getEntryDestinationBounds() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: getEntryDestinationBounds()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: getEntryDestinationBounds()", TAG);
+
updateExpandedPipSize();
final boolean isPipExpanded = mTvPipBoundsState.isTvExpandedPipSupported()
&& mTvPipBoundsState.getDesiredTvExpandedAspectRatio() != 0
@@ -107,10 +107,8 @@
/** Returns the current bounds adjusted to the new aspect ratio, if valid. */
@Override
public Rect getAdjustedDestinationBounds(Rect currentBounds, float newAspectRatio) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: getAdjustedDestinationBounds: %f", TAG, newAspectRatio);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: getAdjustedDestinationBounds: %f", TAG, newAspectRatio);
return adjustBoundsForTemporaryDecor(getTvPipPlacement().getBounds());
}
@@ -154,24 +152,22 @@
restrictedKeepClearAreas,
unrestrictedKeepClearAreas);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: screenSize: %s", TAG, screenSize);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: stashOffset: %d", TAG, mTvPipBoundsState.getStashOffset());
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: insetBounds: %s", TAG, insetBounds.toShortString());
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: pipSize: %s", TAG, pipSize);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: gravity: %s", TAG, Gravity.toString(mTvPipBoundsState.getTvPipGravity()));
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: restrictedKeepClearAreas: %s", TAG, restrictedKeepClearAreas);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: unrestrictedKeepClearAreas: %s", TAG, unrestrictedKeepClearAreas);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: placement: %s", TAG, placement);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: screenSize: %s", TAG, screenSize);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: stashOffset: %d", TAG, mTvPipBoundsState.getStashOffset());
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: insetBounds: %s", TAG, insetBounds.toShortString());
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: pipSize: %s", TAG, pipSize);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: gravity: %s", TAG, Gravity.toString(mTvPipBoundsState.getTvPipGravity()));
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: restrictedKeepClearAreas: %s", TAG, restrictedKeepClearAreas);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: unrestrictedKeepClearAreas: %s", TAG, unrestrictedKeepClearAreas);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: placement: %s", TAG, placement);
return placement;
}
@@ -180,13 +176,11 @@
* @return previous gravity if it is to be saved, or {@link Gravity#NO_GRAVITY} if not.
*/
int updateGravityOnExpandToggled(int previousGravity, boolean expanding) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateGravityOnExpandToggled(), expanding: %b"
- + ", mOrientation: %d, previous gravity: %s",
- TAG, expanding, mTvPipBoundsState.getTvFixedPipOrientation(),
- Gravity.toString(previousGravity));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateGravityOnExpandToggled(), expanding: %b"
+ + ", mOrientation: %d, previous gravity: %s",
+ TAG, expanding, mTvPipBoundsState.getTvFixedPipOrientation(),
+ Gravity.toString(previousGravity));
if (!mTvPipBoundsState.isTvExpandedPipSupported()) {
return Gravity.NO_GRAVITY;
@@ -237,10 +231,8 @@
}
}
mTvPipBoundsState.setTvPipGravity(updatedGravity);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: new gravity: %s", TAG, Gravity.toString(updatedGravity));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: new gravity: %s", TAG, Gravity.toString(updatedGravity));
return gravityToSave;
}
@@ -249,10 +241,8 @@
* @return true if gravity changed
*/
boolean updateGravity(int keycode) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateGravity, keycode: %d", TAG, keycode);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateGravity, keycode: %d", TAG, keycode);
// Check if position change is valid
if (mTvPipBoundsState.isTvPipExpanded()) {
@@ -309,10 +299,8 @@
if (updatedGravity != currentGravity) {
mTvPipBoundsState.setTvPipGravity(updatedGravity);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: new gravity: %s", TAG, Gravity.toString(updatedGravity));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: new gravity: %s", TAG, Gravity.toString(updatedGravity));
return true;
}
return false;
@@ -343,8 +331,8 @@
final Size expandedSize;
if (expandedRatio == 0) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateExpandedPipSize(): Expanded mode aspect ratio"
- + " of 0 not supported", TAG);
+ "%s: updateExpandedPipSize(): Expanded mode aspect ratio"
+ + " of 0 not supported", TAG);
return;
} else if (expandedRatio < 1) {
// vertical
@@ -356,16 +344,12 @@
float aspectRatioHeight = mFixedExpandedWidthInPx / expandedRatio;
if (maxHeight > aspectRatioHeight) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Accommodate aspect ratio", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Accommodate aspect ratio", TAG);
expandedSize = new Size(mFixedExpandedWidthInPx, (int) aspectRatioHeight);
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Aspect ratio is too extreme, use max size", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Aspect ratio is too extreme, use max size", TAG);
expandedSize = new Size(mFixedExpandedWidthInPx, maxHeight);
}
}
@@ -378,26 +362,20 @@
- pipDecorations.left - pipDecorations.right;
float aspectRatioWidth = mFixedExpandedHeightInPx * expandedRatio;
if (maxWidth > aspectRatioWidth) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Accommodate aspect ratio", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Accommodate aspect ratio", TAG);
expandedSize = new Size((int) aspectRatioWidth, mFixedExpandedHeightInPx);
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Aspect ratio is too extreme, use max size", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Aspect ratio is too extreme, use max size", TAG);
expandedSize = new Size(maxWidth, mFixedExpandedHeightInPx);
}
}
}
mTvPipBoundsState.setTvExpandedSize(expandedSize);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateExpandedPipSize(): expanded size, width: %d, height: %d",
- TAG, expandedSize.getWidth(), expandedSize.getHeight());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateExpandedPipSize(): expanded size, width: %d, height: %d",
+ TAG, expandedSize.getWidth(), expandedSize.getHeight());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index b212ea9..b189163 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -39,7 +39,6 @@
* Manages debouncing of PiP movements and scheduling of unstashing.
*/
public class TvPipBoundsController {
- private static final boolean DEBUG = false;
private static final String TAG = "TvPipBoundsController";
/**
@@ -133,11 +132,9 @@
private void schedulePinnedStackPlacement(@NonNull final Placement placement,
int animationDuration) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: schedulePinnedStackPlacement() - pip bounds: %s",
- TAG, placement.getBounds().toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: schedulePinnedStackPlacement() - pip bounds: %s",
+ TAG, placement.getBounds().toShortString());
if (mPendingPlacement != null && Objects.equals(mPendingPlacement.getBounds(),
placement.getBounds())) {
@@ -171,10 +168,8 @@
}
private void applyPendingPlacement() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: applyPendingPlacement()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: applyPendingPlacement()", TAG);
if (mPendingPlacement != null) {
applyPlacement(mPendingPlacement, mPendingStash, mPendingPlacementAnimationDuration);
mPendingStash = false;
@@ -226,10 +221,8 @@
}
mPipTargetBounds = bounds;
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: movePipTo() - new pip bounds: %s", TAG, bounds.toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: movePipTo() - new pip bounds: %s", TAG, bounds.toShortString());
if (mListener != null) {
mListener.onPipTargetBoundsChange(bounds, animationDuration);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 4e1b046..85a544b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -70,7 +70,6 @@
TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener,
ConfigurationChangeListener, UserChangeListener {
private static final String TAG = "TvPipController";
- static final boolean DEBUG = false;
private static final int NONEXISTENT_TASK_ID = -1;
@@ -80,7 +79,8 @@
STATE_PIP,
STATE_PIP_MENU,
})
- public @interface State {}
+ public @interface State {
+ }
/**
* State when there is no applications in Pip.
@@ -117,7 +117,8 @@
private final ShellExecutor mMainExecutor;
private final TvPipImpl mImpl = new TvPipImpl();
- private @State int mState = STATE_NO_PIP;
+ @State
+ private int mState = STATE_NO_PIP;
private int mPreviousGravity = TvPipBoundsState.DEFAULT_TV_GRAVITY;
private int mPinnedTaskId = NONEXISTENT_TASK_ID;
@@ -236,16 +237,12 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onConfigurationChanged(), state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onConfigurationChanged(), state=%s", TAG, stateToName(mState));
if (isPipShown()) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: > closing Pip.", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: > closing Pip.", TAG);
closePip();
}
@@ -268,16 +265,12 @@
*/
@Override
public void showPictureInPictureMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showPictureInPictureMenu(), state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showPictureInPictureMenu(), state=%s", TAG, stateToName(mState));
if (mState == STATE_NO_PIP) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: > cannot open Menu from the current state.", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: > cannot open Menu from the current state.", TAG);
return;
}
@@ -288,10 +281,8 @@
@Override
public void onMenuClosed() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: closeMenu(), state before=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: closeMenu(), state before=%s", TAG, stateToName(mState));
setState(STATE_PIP);
updatePinnedStackBounds();
}
@@ -306,10 +297,8 @@
*/
@Override
public void movePipToFullscreen() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: movePipToFullscreen(), state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: movePipToFullscreen(), state=%s", TAG, stateToName(mState));
mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
onPipDisappeared();
@@ -317,10 +306,8 @@
@Override
public void togglePipExpansion() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: togglePipExpansion()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: togglePipExpansion()", TAG);
boolean expanding = !mTvPipBoundsState.isTvPipExpanded();
int saveGravity = mTvPipBoundsAlgorithm
.updateGravityOnExpandToggled(mPreviousGravity, expanding);
@@ -347,10 +334,8 @@
mPreviousGravity = Gravity.NO_GRAVITY;
updatePinnedStackBounds();
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Position hasn't changed", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Position hasn't changed", TAG);
}
}
@@ -403,11 +388,9 @@
*/
@Override
public void closePip() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: closePip(), state=%s, loseAction=%s", TAG, stateToName(mState),
- mCloseAction);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: closePip(), state=%s, loseAction=%s", TAG, stateToName(mState),
+ mCloseAction);
if (mCloseAction != null) {
try {
@@ -443,10 +426,8 @@
private void checkIfPinnedTaskAppeared() {
final TaskInfo pinnedTask = getPinnedTaskInfo();
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: checkIfPinnedTaskAppeared(), task=%s", TAG, pinnedTask);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: checkIfPinnedTaskAppeared(), task=%s", TAG, pinnedTask);
if (pinnedTask == null || pinnedTask.topActivity == null) return;
mPinnedTaskId = pinnedTask.taskId;
@@ -455,10 +436,8 @@
}
private void checkIfPinnedTaskIsGone() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onTaskStackChanged()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onTaskStackChanged()", TAG);
if (isPipShown() && getPinnedTaskInfo() == null) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -468,10 +447,8 @@
}
private void onPipDisappeared() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipDisappeared() state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipDisappeared() state=%s", TAG, stateToName(mState));
mPipNotificationController.dismiss();
mTvPipMenuController.closeMenu();
@@ -483,19 +460,15 @@
@Override
public void onPipTransitionStarted(int direction, Rect pipBounds) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipTransition_Started(), state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipTransition_Started(), state=%s", TAG, stateToName(mState));
mTvPipMenuController.notifyPipAnimating(true);
}
@Override
public void onPipTransitionCanceled(int direction) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipTransition_Canceled(), state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipTransition_Canceled(), state=%s", TAG, stateToName(mState));
mTvPipMenuController.notifyPipAnimating(false);
}
@@ -504,19 +477,15 @@
if (PipAnimationController.isInPipDirection(direction) && mState == STATE_NO_PIP) {
setState(STATE_PIP);
}
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipTransition_Finished(), state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipTransition_Finished(), state=%s", TAG, stateToName(mState));
mTvPipMenuController.notifyPipAnimating(false);
}
private void setState(@State int state) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setState(), state=%s, prev=%s",
- TAG, stateToName(state), stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: setState(), state=%s, prev=%s",
+ TAG, stateToName(state), stateToName(mState));
mState = state;
}
@@ -550,11 +519,8 @@
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
if (task.getWindowingMode() == WINDOWING_MODE_PINNED) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPinnedActivityRestartAttempt()", TAG);
- }
-
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPinnedActivityRestartAttempt()", TAG);
// If the "Pip-ed" Activity is launched again by Launcher or intent, make it
// fullscreen.
movePipToFullscreen();
@@ -569,7 +535,7 @@
public void onActionsChanged(List<RemoteAction> actions,
RemoteAction closeAction) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onActionsChanged()", TAG);
+ "%s: onActionsChanged()", TAG);
mTvPipMenuController.setAppActions(actions, closeAction);
mCloseAction = closeAction;
@@ -578,7 +544,7 @@
@Override
public void onAspectRatioChanged(float ratio) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onAspectRatioChanged: %f", TAG, ratio);
+ "%s: onAspectRatioChanged: %f", TAG, ratio);
mTvPipBoundsState.setAspectRatio(ratio);
if (!mTvPipBoundsState.isTvPipExpanded()) {
@@ -589,7 +555,7 @@
@Override
public void onExpandedAspectRatioChanged(float ratio) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onExpandedAspectRatioChanged: %f", TAG, ratio);
+ "%s: onExpandedAspectRatioChanged: %f", TAG, ratio);
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(ratio, false);
mTvPipMenuController.updateExpansionState();
@@ -635,11 +601,9 @@
wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedTaskListener() {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onImeVisibilityChanged(), visible=%b, height=%d",
- TAG, imeVisible, imeHeight);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onImeVisibilityChanged(), visible=%b, height=%d",
+ TAG, imeVisible, imeHeight);
if (imeVisible == mTvPipBoundsState.isImeShowing()
&& (!imeVisible || imeHeight == mTvPipBoundsState.getImeHeight())) {
@@ -661,17 +625,13 @@
}
private static TaskInfo getPinnedTaskInfo() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: getPinnedTaskInfo()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: getPinnedTaskInfo()", TAG);
try {
final TaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: taskInfo=%s", TAG, taskInfo);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: taskInfo=%s", TAG, taskInfo);
return taskInfo;
} catch (RemoteException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -681,10 +641,8 @@
}
private static void removeTask(int taskId) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: removeTask(), taskId=%d", TAG, taskId);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: removeTask(), taskId=%d", TAG, taskId);
try {
ActivityTaskManager.getService().removeTask(taskId);
} catch (Exception e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index a4e9062..176fdfe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -54,7 +54,6 @@
*/
public class TvPipMenuController implements PipMenuController, TvPipMenuView.Listener {
private static final String TAG = "TvPipMenuController";
- private static final boolean DEBUG = TvPipController.DEBUG;
private static final String BACKGROUND_WINDOW_TITLE = "PipBackgroundView";
private final Context mContext;
@@ -119,10 +118,8 @@
}
void setDelegate(Delegate delegate) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setDelegate(), delegate=%s", TAG, delegate);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: setDelegate(), delegate=%s", TAG, delegate);
if (mDelegate != null) {
throw new IllegalStateException(
"The delegate has already been set and should not change.");
@@ -145,10 +142,8 @@
}
private void attachPipMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: attachPipMenu()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: attachPipMenu()", TAG);
if (mPipMenuView != null) {
detachPipMenu();
@@ -158,7 +153,7 @@
attachPipMenuView();
mTvPipBoundsState.setPipMenuPermanentDecorInsets(Insets.of(-mPipMenuBorderWidth,
- -mPipMenuBorderWidth, -mPipMenuBorderWidth, -mPipMenuBorderWidth));
+ -mPipMenuBorderWidth, -mPipMenuBorderWidth, -mPipMenuBorderWidth));
mTvPipBoundsState.setPipMenuTemporaryDecorInsets(Insets.of(0, 0, 0, -mPipEduTextHeight));
mMainHandler.postDelayed(mCloseEduTextRunnable, mPipEduTextShowDurationMs);
}
@@ -180,15 +175,15 @@
private void setUpViewSurfaceZOrder(View v, int zOrderRelativeToPip) {
v.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- v.getViewRootImpl().addSurfaceChangedCallback(
- new PipMenuSurfaceChangedCallback(v, zOrderRelativeToPip));
- }
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ v.getViewRootImpl().addSurfaceChangedCallback(
+ new PipMenuSurfaceChangedCallback(v, zOrderRelativeToPip));
+ }
- @Override
- public void onViewDetachedFromWindow(View v) {
- }
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
});
}
@@ -205,10 +200,8 @@
}
void showMovementMenuOnly() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showMovementMenuOnly()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showMovementMenuOnly()", TAG);
setInMoveMode(true);
mCloseAfterExitMoveMenu = true;
showMenuInternal();
@@ -216,9 +209,7 @@
@Override
public void showMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG);
setInMoveMode(false);
mCloseAfterExitMoveMenu = false;
showMenuInternal();
@@ -274,15 +265,12 @@
}
void closeMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: closeMenu()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: closeMenu()", TAG);
if (mPipMenuView == null) {
return;
}
-
mPipMenuView.hideAllUserControls();
grantPipMenuFocus(false);
mDelegate.onMenuClosed();
@@ -296,7 +284,6 @@
if (mInMoveMode == moveMode) {
return;
}
-
mInMoveMode = moveMode;
if (mDelegate != null) {
mDelegate.onInMoveModeChanged();
@@ -305,22 +292,19 @@
@Override
public void onEnterMoveMode() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onEnterMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
- mCloseAfterExitMoveMenu);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onEnterMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
+ mCloseAfterExitMoveMenu);
setInMoveMode(true);
mPipMenuView.showMoveMenu(mDelegate.getPipGravity());
}
@Override
public boolean onExitMoveMode() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onExitMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
- mCloseAfterExitMoveMenu);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onExitMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
+ mCloseAfterExitMoveMenu);
+
if (mCloseAfterExitMoveMenu) {
setInMoveMode(false);
mCloseAfterExitMoveMenu = false;
@@ -337,10 +321,8 @@
@Override
public boolean onPipMovement(int keycode) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipMovement - %b", TAG, mInMoveMode);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipMovement - %b", TAG, mInMoveMode);
if (mInMoveMode) {
mDelegate.movePip(keycode);
}
@@ -357,18 +339,14 @@
@Override
public void setAppActions(List<RemoteAction> actions, RemoteAction closeAction) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setAppActions()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: setAppActions()", TAG);
updateAdditionalActionsList(mAppActions, actions, closeAction);
}
private void onMediaActionsChanged(List<RemoteAction> actions) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onMediaActionsChanged()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onMediaActionsChanged()", TAG);
// Hide disabled actions.
List<RemoteAction> enabledActions = new ArrayList<>();
@@ -420,10 +398,8 @@
public void resizePipMenu(@Nullable SurfaceControl pipLeash,
@Nullable SurfaceControl.Transaction t,
Rect destinationBounds) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: resizePipMenu: %s", TAG, destinationBounds.toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: resizePipMenu: %s", TAG, destinationBounds.toShortString());
if (destinationBounds.isEmpty()) {
return;
}
@@ -437,14 +413,14 @@
final SurfaceControl frontSurface = getSurfaceControl(mPipMenuView);
final SyncRtSurfaceTransactionApplier.SurfaceParams frontParams =
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(frontSurface)
- .withWindowCrop(menuBounds)
- .build();
+ .withWindowCrop(menuBounds)
+ .build();
final SurfaceControl backSurface = getSurfaceControl(mPipBackgroundView);
final SyncRtSurfaceTransactionApplier.SurfaceParams backParams =
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(backSurface)
- .withWindowCrop(menuBounds)
- .build();
+ .withWindowCrop(menuBounds)
+ .build();
// TODO(b/226580399): switch to using SurfaceSyncer (see b/200284684) to synchronize the
// animations of the pip surface with the content of the front and back menu surfaces
@@ -467,13 +443,11 @@
@Override
public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction transaction,
Rect pipDestBounds) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: movePipMenu: %s", TAG, pipDestBounds.toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: movePipMenu: %s", TAG, pipDestBounds.toShortString());
if (pipDestBounds.isEmpty()) {
- if (transaction == null && DEBUG) {
+ if (transaction == null) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: no transaction given", TAG);
}
@@ -489,16 +463,12 @@
// resizing and the PiP menu is also resized. We then want to do a scale from the current
// new menu bounds.
if (pipLeash != null && transaction != null) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: tmpSourceBounds based on mPipMenuView.getBoundsOnScreen()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: tmpSourceBounds based on mPipMenuView.getBoundsOnScreen()", TAG);
mPipMenuView.getBoundsOnScreen(tmpSourceBounds);
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: tmpSourceBounds based on menu width and height", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: tmpSourceBounds based on menu width and height", TAG);
tmpSourceBounds.set(0, 0, menuDestBounds.width(), menuDestBounds.height());
}
@@ -524,8 +494,8 @@
if (pipLeash != null && transaction != null) {
final SyncRtSurfaceTransactionApplier.SurfaceParams pipParams =
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash)
- .withMergeTransaction(transaction)
- .build();
+ .withMergeTransaction(transaction)
+ .build();
mApplier.scheduleApply(frontParams, pipParams);
} else {
mApplier.scheduleApply(frontParams);
@@ -567,10 +537,8 @@
@Override
public void updateMenuBounds(Rect destinationBounds) {
final Rect menuBounds = calculateMenuSurfaceBounds(destinationBounds);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateMenuBounds: %s", TAG, menuBounds.toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateMenuBounds: %s", TAG, menuBounds.toShortString());
mSystemWindows.updateViewLayout(mPipBackgroundView,
getPipMenuLayoutParams(mContext, BACKGROUND_WINDOW_TITLE, menuBounds.width(),
menuBounds.height()));
@@ -629,10 +597,8 @@
}
private void grantPipMenuFocus(boolean grantFocus) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: grantWindowFocus(%b)", TAG, grantFocus);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: grantWindowFocus(%b)", TAG, grantFocus);
try {
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 97e017a..9cd05b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -71,7 +71,6 @@
*/
public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private static final String TAG = "TvPipMenuView";
- private static final boolean DEBUG = TvPipController.DEBUG;
private static final int FIRST_CUSTOM_ACTION_POSITION = 3;
@@ -448,10 +447,8 @@
}
void setIsExpanded(boolean expanded) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setIsExpanded, expanded: %b", TAG, expanded);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: setIsExpanded, expanded: %b", TAG, expanded);
mExpandButton.setImageResource(
expanded ? R.drawable.pip_ic_collapse : R.drawable.pip_ic_expand);
mExpandButton.setTextAndDescription(
@@ -462,9 +459,7 @@
* @param gravity for the arrow hints
*/
void showMoveMenu(int gravity) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG);
mButtonMenuIsVisible = false;
mMoveMenuIsVisible = true;
showButtonsMenu(false);
@@ -476,11 +471,8 @@
}
void showButtonsMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showButtonsMenu()", TAG);
- }
-
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showButtonsMenu()", TAG);
mButtonMenuIsVisible = true;
mMoveMenuIsVisible = false;
showButtonsMenu(true);
@@ -553,10 +545,8 @@
*/
void setAdditionalActions(List<RemoteAction> actions, RemoteAction closeAction,
Handler mainHandler) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setAdditionalActions()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: setAdditionalActions()", TAG);
// Replace system close action with custom close action if available
if (closeAction != null) {
@@ -707,10 +697,8 @@
* Shows user hints for moving the PiP, e.g. arrows.
*/
public void showMovementHints(int gravity) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showMovementHints(), position: %s", TAG, Gravity.toString(gravity));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showMovementHints(), position: %s", TAG, Gravity.toString(gravity));
animateAlphaTo(checkGravity(gravity, Gravity.BOTTOM) ? 1f : 0f, mArrowUp);
animateAlphaTo(checkGravity(gravity, Gravity.TOP) ? 1f : 0f, mArrowDown);
@@ -752,10 +740,9 @@
* Hides user hints for moving the PiP, e.g. arrows.
*/
public void hideMovementHints() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: hideMovementHints()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: hideMovementHints()", TAG);
+
animateAlphaTo(0, mArrowUp);
animateAlphaTo(0, mArrowRight);
animateAlphaTo(0, mArrowDown);
@@ -767,10 +754,8 @@
* Show or hide the pip buttons menu.
*/
public void showButtonsMenu(boolean show) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showUserActions: %b", TAG, show);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showUserActions: %b", TAG, show);
if (show) {
mActionButtonsContainer.setVisibility(VISIBLE);
refocusPreviousButton();
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 93c7529..3fef823 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
@@ -32,7 +32,7 @@
Consts.TAG_WM_SHELL),
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
- WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_STARTING_WINDOW),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 7b42350..27bc1a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
@@ -52,6 +53,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -82,6 +84,15 @@
private final Map<Integer, SplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
+ * Set of taskId's that have been launched in freeform mode.
+ * This includes tasks that are currently running, visible and in freeform mode. And also
+ * includes tasks that are running in the background, are no longer visible, but at some point
+ * were visible to the user.
+ * This is used to decide which freeform apps belong to the user's desktop.
+ */
+ private final HashSet<Integer> mActiveFreeformTasks = new HashSet<>();
+
+ /**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
* supported.
*/
@@ -206,6 +217,22 @@
notifyRecentTasksChanged();
}
+ /**
+ * Mark a task with given {@code taskId} as active in freeform
+ */
+ public void addActiveFreeformTask(int taskId) {
+ mActiveFreeformTasks.add(taskId);
+ notifyRecentTasksChanged();
+ }
+
+ /**
+ * Remove task with given {@code taskId} from active freeform tasks
+ */
+ public void removeActiveFreeformTask(int taskId) {
+ mActiveFreeformTasks.remove(taskId);
+ notifyRecentTasksChanged();
+ }
+
@VisibleForTesting
void notifyRecentTasksChanged() {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed");
@@ -273,6 +300,9 @@
rawMapping.put(taskInfo.taskId, taskInfo);
}
+ boolean desktopModeActive = DesktopMode.isActive(mContext);
+ ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
+
// Pull out the pairs as we iterate back in the list
ArrayList<GroupedRecentTaskInfo> recentTasks = new ArrayList<>();
for (int i = 0; i < rawList.size(); i++) {
@@ -282,16 +312,31 @@
continue;
}
+ if (desktopModeActive && mActiveFreeformTasks.contains(taskInfo.taskId)) {
+ // Freeform tasks will be added as a separate entry
+ freeformTasks.add(taskInfo);
+ continue;
+ }
+
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
- if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(pairedTaskId)) {
+ if (!desktopModeActive && pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
+ pairedTaskId)) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
- recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
+ recentTasks.add(GroupedRecentTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- recentTasks.add(new GroupedRecentTaskInfo(taskInfo));
+ recentTasks.add(GroupedRecentTaskInfo.forSingleTask(taskInfo));
}
}
+
+ // Add a special entry for freeform tasks
+ if (!freeformTasks.isEmpty()) {
+ // First task is added separately
+ recentTasks.add(0, GroupedRecentTaskInfo.forFreeformTasks(
+ freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0])));
+ }
+
return recentTasks;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 51921e7..3714fe7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.view.RemoteAnimationAdapter;
@@ -89,7 +90,7 @@
float splitRatio, in RemoteAnimationAdapter adapter) = 11;
/**
- * Start a pair of intent and task using legacy transition system.
+ * Starts a pair of intent and task using legacy transition system.
*/
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions,
@@ -108,4 +109,11 @@
* does not expect split to currently be running.
*/
RemoteAnimationTarget[] onStartingSplitLegacy(in RemoteAnimationTarget[] appTargets) = 14;
+
+ /**
+ * Starts a pair of shortcut and task using legacy transition system.
+ */
+ oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, int taskId,
+ in Bundle mainOptions, in Bundle sideOptions, int sidePosition, float splitRatio,
+ in RemoteAnimationAdapter adapter) = 15;
}
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 2117b69..8729161 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
@@ -40,6 +40,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
@@ -212,14 +213,18 @@
mShellController.addKeyguardChangeListener(this);
if (mStageCoordinator == null) {
// TODO: Multi-display
- mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
- mTaskOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mIconProvider, mMainExecutor, mRecentTasksOptional);
+ mStageCoordinator = createStageCoordinator();
}
mDragAndDropController.setSplitScreenController(this);
}
+ protected StageCoordinator createStageCoordinator() {
+ return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
+ mTaskOrganizer, mDisplayController, mDisplayImeController,
+ mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
+ mIconProvider, mMainExecutor, mRecentTasksOptional);
+ }
+
@Override
public Context getContext() {
return mContext;
@@ -788,6 +793,17 @@
}
@Override
+ public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
+ int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
+ @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ executeRemoteCallWithTaskPermission(mController,
+ "startShortcutAndTaskWithLegacyTransition", (controller) ->
+ controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
+ shortcutInfo, taskId, mainOptions, sideOptions, sidePosition,
+ splitRatio, adapter));
+ }
+
+ @Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio,
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 c08aa5a..f8b40be 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
@@ -69,11 +69,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
+import android.app.IActivityTaskManager;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
@@ -81,11 +82,11 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
import android.util.Slog;
import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -245,7 +246,7 @@
}
};
- StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
+ protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
@@ -531,133 +532,136 @@
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
float splitRatio, RemoteAnimationAdapter adapter) {
- startWithLegacyTransition(mainTaskId, sideTaskId, null /* pendingIntent */,
- null /* fillInIntent */, mainOptions, sideOptions, sidePosition, splitRatio,
- adapter);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (sideOptions == null) sideOptions = new Bundle();
+ addActivityOptions(sideOptions, mSideStage);
+ wct.startTask(sideTaskId, sideOptions);
+
+ startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter);
}
/** Start an intent and a task ordered by {@code intentFirst}. */
void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
- startWithLegacyTransition(taskId, INVALID_TASK_ID, pendingIntent, fillInIntent,
- mainOptions, sideOptions, sidePosition, splitRatio, adapter);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (sideOptions == null) sideOptions = new Bundle();
+ addActivityOptions(sideOptions, mSideStage);
+ wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
+
+ startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
}
- private void startWithLegacyTransition(int mainTaskId, int sideTaskId,
- @Nullable PendingIntent pendingIntent, @Nullable Intent fillInIntent,
- @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
+ void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
+ int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
- final boolean withIntent = pendingIntent != null && fillInIntent != null;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (sideOptions == null) sideOptions = new Bundle();
+ addActivityOptions(sideOptions, mSideStage);
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo, sideOptions);
+
+ startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
+ }
+
+ private void startWithLegacyTransition(WindowContainerTransaction sideWct, int mainTaskId,
+ @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
+ RemoteAnimationAdapter adapter) {
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
+ mSplitLayout.setDivideRatio(splitRatio);
+
// Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = true;
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
- prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
- // Need to add another wrapper here in shell so that we can inject the divider bar
- // and also manage the process elevation via setRunningRemote
- IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+
+ LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
@Override
- public void onAnimationStart(@WindowManager.TransitionOldType int transit,
- RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers,
- RemoteAnimationTarget[] nonApps,
- final IRemoteAnimationFinishedCallback finishedCallback) {
- RemoteAnimationTarget[] augmentedNonApps =
- new RemoteAnimationTarget[nonApps.length + 1];
- for (int i = 0; i < nonApps.length; ++i) {
- augmentedNonApps[i] = nonApps[i];
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback,
+ SurfaceControl.Transaction t) {
+ if (apps == null || apps.length == 0) {
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ setDividerVisibility(true, t);
+ t.apply();
+ onRemoteAnimationFinished(apps);
+ try {
+ adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ return;
}
- augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+
+ // Wrap the divider bar into non-apps target to animate together.
+ nonApps = ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
+ getDividerBarLegacyTarget());
+
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ setDividerVisibility(true, t);
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) {
+ t.show(apps[i].leash);
+ // Reset the surface position of the opening app to prevent double-offset.
+ t.setPosition(apps[i].leash, 0, 0);
+ }
+ }
+ t.apply();
IRemoteAnimationFinishedCallback wrapCallback =
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
- onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct);
+ onRemoteAnimationFinished(apps);
finishedCallback.onAnimationFinished();
}
};
Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
try {
- adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
- augmentedNonApps, wrapCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
- }
-
- @Override
- public void onAnimationCancelled(boolean isKeyguardOccluded) {
- onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
- try {
- adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
+ adapter.getRunner().onAnimationStart(
+ transit, apps, wallpapers, nonApps, wrapCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e);
}
}
};
- RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
- wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
- if (mainOptions == null) {
- mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
- } else {
- ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
- mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
- mainOptions = mainActivityOptions.toBundle();
- }
-
- sideOptions = sideOptions != null ? sideOptions : new Bundle();
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
setSideStagePosition(sidePosition, wct);
-
- mSplitLayout.setDivideRatio(splitRatio);
if (!mMainStage.isActive()) {
- // Build a request WCT that will launch both apps such that task 0 is on the main stage
- // while task 1 is on the side stage.
mMainStage.activate(wct, false /* reparent */);
}
+
+ if (mainOptions == null) mainOptions = new Bundle();
+ addActivityOptions(mainOptions, mMainStage);
+ wct.startTask(mainTaskId, mainOptions);
+ wct.merge(sideWct, true);
+
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
wct.setForceTranslucent(mRootTaskInfo.token, false);
- // Make sure the launch options will put tasks in the corresponding split roots
- addActivityOptions(mainOptions, mMainStage);
- addActivityOptions(sideOptions, mSideStage);
-
- // Add task launch requests
- wct.startTask(mainTaskId, mainOptions);
- if (withIntent) {
- wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
- } else {
- wct.startTask(sideTaskId, sideOptions);
- }
-
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> {
- setDividerVisibility(true, t);
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
- });
+ mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
- private void onRemoteAnimationFinishedOrCancelled(boolean cancel,
- WindowContainerTransaction evictWct) {
+ private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
- // If any stage has no child after animation finished, it means that split will display
- // nothing, such status will happen if task and intent is same app but not support
- // multi-instance, we should exit split and expand that app as full screen.
- if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
- mMainExecutor.execute(() ->
- exitSplitScreen(mMainStage.getChildCount() == 0
- ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
- } else {
- mSyncQueue.queue(evictWct);
+ if (apps == null || apps.length == 0) return;
+
+ // If any stage has no child after finished animation, that side of the split will display
+ // nothing. This might happen if starting the same app on the both sides while not
+ // supporting multi-instance. Exit the split screen and expand that app to full screen.
+ if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+ mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
+ ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+ return;
}
+
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
+ prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
+ mSyncQueue.queue(evictWct);
}
/**
@@ -878,6 +882,8 @@
WindowContainerTransaction wct, @ExitReason int exitReason) {
if (!mMainStage.isActive() || mIsExiting) return;
+ onSplitScreenExit();
+
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
@@ -947,6 +953,47 @@
}
/**
+ * Overridden by child classes.
+ */
+ protected void onSplitScreenEnter() {
+ }
+
+ /**
+ * Overridden by child classes.
+ */
+ protected void onSplitScreenExit() {
+ }
+
+ /**
+ * Exits the split screen by finishing one of the tasks.
+ */
+ protected void exitStage(@SplitPosition int stageToClose) {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ StageTaskListener stageToTop = mSideStagePosition == stageToClose
+ ? mMainStage
+ : mSideStage;
+ exitSplitScreen(stageToTop, EXIT_REASON_APP_FINISHED);
+ } else {
+ boolean toEnd = stageToClose == SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ mSplitLayout.flingDividerToDismiss(toEnd, EXIT_REASON_APP_FINISHED);
+ }
+ }
+
+ /**
+ * Grants focus to the main or the side stages.
+ */
+ protected void grantFocusToStage(@SplitPosition int stageToFocus) {
+ IActivityTaskManager activityTaskManagerService = IActivityTaskManager.Stub.asInterface(
+ ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE));
+ try {
+ activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
+ } catch (RemoteException | NullPointerException e) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "%s: Unable to update focus on the chosen stage, %s", TAG, e);
+ }
+ }
+
+ /**
* Returns whether the split pair in the recent tasks list should be broken.
*/
private boolean shouldBreakPairedTaskInRecents(@ExitReason int exitReason) {
@@ -993,6 +1040,7 @@
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
if (mMainStage.isActive()) return;
+ onSplitScreenEnter();
if (taskInfo != null) {
setSideStagePosition(startPosition, wct);
mSideStage.addTask(taskInfo, wct);
@@ -1386,6 +1434,7 @@
}
} else if (isSideStage && hasChildren && !mMainStage.isActive()) {
// TODO (b/238697912) : Add the validation to prevent entering non-recovered status
+ onSplitScreenEnter();
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init();
mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
index d325d16..28be0ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
@@ -1,2 +1,3 @@
# WM shell sub-module TV splitscreen owner
galinap@google.com
+bronger@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
new file mode 100644
index 0000000..1d8a8d5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen.tv;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.split.SplitScreenConstants;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+/**
+ * Handles the interaction logic with the {@link TvSplitMenuView}.
+ * A bridge between {@link TvStageCoordinator} and {@link TvSplitMenuView}.
+ */
+public class TvSplitMenuController implements TvSplitMenuView.Listener {
+
+ private static final String TAG = TvSplitMenuController.class.getSimpleName();
+ private static final String ACTION_SHOW_MENU = "com.android.wm.shell.splitscreen.SHOW_MENU";
+ private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
+
+ private final Context mContext;
+ private final StageController mStageController;
+ private final SystemWindows mSystemWindows;
+ private final Handler mMainHandler;
+
+ private final TvSplitMenuView mSplitMenuView;
+
+ private final ActionBroadcastReceiver mActionBroadcastReceiver;
+
+ private final int mTvButtonFadeAnimationDuration;
+
+ public TvSplitMenuController(Context context, StageController stageController,
+ SystemWindows systemWindows, Handler mainHandler) {
+ mContext = context;
+ mMainHandler = mainHandler;
+ mStageController = stageController;
+ mSystemWindows = systemWindows;
+
+ mTvButtonFadeAnimationDuration = context.getResources()
+ .getInteger(R.integer.tv_window_menu_fade_animation_duration);
+
+ mSplitMenuView = (TvSplitMenuView) LayoutInflater.from(context)
+ .inflate(R.layout.tv_split_menu_view, null);
+ mSplitMenuView.setListener(this);
+
+ mActionBroadcastReceiver = new ActionBroadcastReceiver();
+ }
+
+ /**
+ * Adds the menu view for the splitscreen to SystemWindows.
+ */
+ void addSplitMenuViewToSystemWindows() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ mContext.getResources().getDisplayMetrics().widthPixels,
+ mContext.getResources().getDisplayMetrics().heightPixels,
+ TYPE_DOCK_DIVIDER,
+ FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ mSplitMenuView.setAlpha(0);
+ mSystemWindows.addView(mSplitMenuView, lp, DEFAULT_DISPLAY, SHELL_ROOT_LAYER_DIVIDER);
+ }
+
+ /**
+ * Removes the menu view for the splitscreen from SystemWindows.
+ */
+ void removeSplitMenuViewFromSystemWindows() {
+ mSystemWindows.removeView(mSplitMenuView);
+ }
+
+ /**
+ * Registers BroadcastReceiver when split screen mode is entered.
+ */
+ void registerBroadcastReceiver() {
+ mActionBroadcastReceiver.register();
+ }
+
+ /**
+ * Unregisters BroadcastReceiver when split screen mode is entered.
+ */
+ void unregisterBroadcastReceiver() {
+ mActionBroadcastReceiver.unregister();
+ }
+
+ @Override
+ public void onBackPress() {
+ setMenuVisibility(false);
+ }
+
+ @Override
+ public void onFocusStage(@SplitScreenConstants.SplitPosition int stageToFocus) {
+ setMenuVisibility(false);
+ mStageController.grantFocusToStage(stageToFocus);
+ }
+
+ @Override
+ public void onCloseStage(@SplitScreenConstants.SplitPosition int stageToClose) {
+ setMenuVisibility(false);
+ mStageController.exitStage(stageToClose);
+ }
+
+ @Override
+ public void onSwapPress() {
+ mStageController.swapStages();
+ }
+
+ private void setMenuVisibility(boolean visible) {
+ applyMenuVisibility(visible);
+ setMenuFocus(visible);
+ }
+
+ private void applyMenuVisibility(boolean visible) {
+ float alphaTarget = visible ? 1F : 0F;
+
+ if (mSplitMenuView.getAlpha() == alphaTarget) {
+ return;
+ }
+
+ mSplitMenuView.animate()
+ .alpha(alphaTarget)
+ .setDuration(mTvButtonFadeAnimationDuration)
+ .withStartAction(() -> {
+ if (alphaTarget != 0) {
+ mSplitMenuView.setVisibility(VISIBLE);
+ }
+ })
+ .withEndAction(() -> {
+ if (alphaTarget == 0) {
+ mSplitMenuView.setVisibility(INVISIBLE);
+ }
+ });
+
+ }
+
+ private void setMenuFocus(boolean focused) {
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null,
+ mSystemWindows.getFocusGrantToken(mSplitMenuView), focused);
+ } catch (RemoteException e) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "%s: Unable to update focus, %s", TAG, e);
+ }
+ }
+
+ interface StageController {
+ void grantFocusToStage(@SplitScreenConstants.SplitPosition int stageToFocus);
+ void exitStage(@SplitScreenConstants.SplitPosition int stageToClose);
+ void swapStages();
+ }
+
+ private class ActionBroadcastReceiver extends BroadcastReceiver {
+
+ final IntentFilter mIntentFilter;
+ {
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(ACTION_SHOW_MENU);
+ }
+ boolean mRegistered = false;
+
+ void register() {
+ if (mRegistered) return;
+
+ mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION,
+ mMainHandler);
+ mRegistered = true;
+ }
+
+ void unregister() {
+ if (!mRegistered) return;
+
+ mContext.unregisterReceiver(this);
+ mRegistered = false;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ if (ACTION_SHOW_MENU.equals(action)) {
+ setMenuVisibility(true);
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java
new file mode 100644
index 0000000..88e9757
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen.tv;
+
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.KeyEvent.KEYCODE_BACK;
+
+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;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.split.SplitScreenConstants;
+
+/**
+ * A View for the Menu Window.
+ */
+public class TvSplitMenuView extends LinearLayout implements View.OnClickListener {
+
+ private Listener mListener;
+
+ public TvSplitMenuView(Context context) {
+ super(context);
+ }
+
+ public TvSplitMenuView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public TvSplitMenuView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ initButtons();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) return;
+
+ final int id = v.getId();
+ if (id == R.id.tv_split_main_menu_focus_button) {
+ mListener.onFocusStage(SPLIT_POSITION_TOP_OR_LEFT);
+ } else if (id == R.id.tv_split_main_menu_close_button) {
+ mListener.onCloseStage(SPLIT_POSITION_TOP_OR_LEFT);
+ } else if (id == R.id.tv_split_side_menu_focus_button) {
+ mListener.onFocusStage(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ } else if (id == R.id.tv_split_side_menu_close_button) {
+ mListener.onCloseStage(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ } else if (id == R.id.tv_split_menu_swap_stages) {
+ mListener.onSwapPress();
+ }
+ }
+
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getAction() == ACTION_DOWN) {
+ if (event.getKeyCode() == KEYCODE_BACK) {
+ if (mListener != null) {
+ mListener.onBackPress();
+ return true;
+ }
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ private void initButtons() {
+ findViewById(R.id.tv_split_main_menu_focus_button).setOnClickListener(this);
+ findViewById(R.id.tv_split_main_menu_close_button).setOnClickListener(this);
+ findViewById(R.id.tv_split_side_menu_focus_button).setOnClickListener(this);
+ findViewById(R.id.tv_split_side_menu_close_button).setOnClickListener(this);
+ findViewById(R.id.tv_split_menu_swap_stages).setOnClickListener(this);
+ }
+
+ void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ interface Listener {
+ /** "Back" button from the remote control */
+ void onBackPress();
+
+ /** Menu Action Buttons */
+
+ void onFocusStage(@SplitScreenConstants.SplitPosition int stageToFocus);
+
+ void onCloseStage(@SplitScreenConstants.SplitPosition int stageToClose);
+
+ void onSwapPress();
+ }
+}
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
new file mode 100644
index 0000000..9285e84
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen.tv;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.splitscreen.SplitscreenEventLogger;
+import com.android.wm.shell.splitscreen.StageCoordinator;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.Optional;
+
+/**
+ * Class inherits from {@link SplitScreenController} and provides {@link TvStageCoordinator}
+ * for Split Screen on TV.
+ */
+public class TvSplitScreenController extends SplitScreenController {
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final SyncTransactionQueue mSyncQueue;
+ private final Context mContext;
+ private final ShellExecutor mMainExecutor;
+ private final DisplayController mDisplayController;
+ private final DisplayImeController mDisplayImeController;
+ private final DisplayInsetsController mDisplayInsetsController;
+ private final Transitions mTransitions;
+ private final TransactionPool mTransactionPool;
+ private final IconProvider mIconProvider;
+ private final Optional<RecentTasksController> mRecentTasksOptional;
+
+ private final Handler mMainHandler;
+ private final SystemWindows mSystemWindows;
+
+ public TvSplitScreenController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
+ DragAndDropController dragAndDropController,
+ Transitions transitions,
+ TransactionPool transactionPool,
+ IconProvider iconProvider,
+ Optional<RecentTasksController> recentTasks,
+ ShellExecutor mainExecutor,
+ Handler mainHandler,
+ SystemWindows systemWindows) {
+ super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
+ syncQueue, rootTDAOrganizer, displayController, displayImeController,
+ displayInsetsController, dragAndDropController, transitions, transactionPool,
+ iconProvider, recentTasks, mainExecutor);
+
+ mTaskOrganizer = shellTaskOrganizer;
+ mSyncQueue = syncQueue;
+ mContext = context;
+ mMainExecutor = mainExecutor;
+ mDisplayController = displayController;
+ mDisplayImeController = displayImeController;
+ mDisplayInsetsController = displayInsetsController;
+ mTransitions = transitions;
+ mTransactionPool = transactionPool;
+ mIconProvider = iconProvider;
+ mRecentTasksOptional = recentTasks;
+
+ mMainHandler = mainHandler;
+ mSystemWindows = systemWindows;
+ }
+
+ /**
+ * Provides Tv-specific StageCoordinator.
+ * @return {@link TvStageCoordinator}
+ */
+ @Override
+ protected StageCoordinator createStageCoordinator() {
+ return new TvStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
+ mTaskOrganizer, mDisplayController, mDisplayImeController,
+ mDisplayInsetsController, mTransitions, mTransactionPool,
+ new SplitscreenEventLogger(), mIconProvider, mMainExecutor, mMainHandler,
+ mRecentTasksOptional, 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
new file mode 100644
index 0000000..6595beb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen.tv;
+
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitScreenConstants;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitscreenEventLogger;
+import com.android.wm.shell.splitscreen.StageCoordinator;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.Optional;
+
+/**
+ * Expands {@link StageCoordinator} functionality with Tv-specific methods.
+ */
+public class TvStageCoordinator extends StageCoordinator
+ implements TvSplitMenuController.StageController {
+
+ private final TvSplitMenuController mTvSplitMenuController;
+
+ public TvStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
+ ShellTaskOrganizer taskOrganizer, DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController, Transitions transitions,
+ TransactionPool transactionPool, SplitscreenEventLogger logger,
+ IconProvider iconProvider, ShellExecutor mainExecutor,
+ Handler mainHandler,
+ Optional<RecentTasksController> recentTasks,
+ SystemWindows systemWindows) {
+ super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
+ displayInsetsController, transitions, transactionPool, logger, iconProvider,
+ mainExecutor, recentTasks);
+
+ mTvSplitMenuController = new TvSplitMenuController(context, this,
+ systemWindows, mainHandler);
+
+ }
+
+ @Override
+ protected void onSplitScreenEnter() {
+ mTvSplitMenuController.addSplitMenuViewToSystemWindows();
+ mTvSplitMenuController.registerBroadcastReceiver();
+ }
+
+ @Override
+ protected void onSplitScreenExit() {
+ mTvSplitMenuController.unregisterBroadcastReceiver();
+ mTvSplitMenuController.removeSplitMenuViewFromSystemWindows();
+ }
+
+ @Override
+ public void grantFocusToStage(@SplitScreenConstants.SplitPosition int stageToFocus) {
+ super.grantFocusToStage(stageToFocus);
+ }
+
+ @Override
+ public void exitStage(@SplitScreenConstants.SplitPosition int stageToClose) {
+ super.exitStage(stageToClose);
+ }
+
+ /**
+ * Swaps the stages inside the SplitLayout.
+ */
+ @Override
+ public void swapStages() {
+ onDoubleTappedDivider();
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index cff60f5..4c927b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -554,6 +554,12 @@
private void edgeExtendWindow(TransitionInfo.Change change,
Animation a, SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction) {
+ // Do not create edge extension surface for transfer starting window change.
+ // The app surface could be empty thus nothing can draw on the hardware renderer, which will
+ // block this thread when calling Surface#unlockCanvasAndPost.
+ if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ return;
+ }
final Transformation transformationAtStart = new Transformation();
a.getTransformationAt(0, transformationAtStart);
final Transformation transformationAtEnd = new Transformation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 6388ca1..b647f43 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -139,39 +139,48 @@
.build();
try {
- SurfaceControl.LayerCaptureArgs args =
- new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
- .setCaptureSecureLayers(true)
- .setAllowProtected(true)
- .setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight))
- .build();
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureLayers(args);
- if (screenshotBuffer == null) {
- Slog.w(TAG, "Unable to take screenshot of display");
- return;
- }
+ if (change.getSnapshot() != null) {
+ mScreenshotLayer = change.getSnapshot();
+ t.reparent(mScreenshotLayer, mAnimLeash);
+ mStartLuma = change.getSnapshotLuma();
+ } else {
+ SurfaceControl.LayerCaptureArgs args =
+ new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+ .setCaptureSecureLayers(true)
+ .setAllowProtected(true)
+ .setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight))
+ .build();
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ SurfaceControl.captureLayers(args);
+ if (screenshotBuffer == null) {
+ Slog.w(TAG, "Unable to take screenshot of display");
+ return;
+ }
- mScreenshotLayer = new SurfaceControl.Builder(session)
- .setParent(mAnimLeash)
- .setBLASTLayer()
- .setSecure(screenshotBuffer.containsSecureLayers())
- .setOpaque(true)
- .setCallsite("ShellRotationAnimation")
- .setName("RotationLayer")
- .build();
+ mScreenshotLayer = new SurfaceControl.Builder(session)
+ .setParent(mAnimLeash)
+ .setBLASTLayer()
+ .setSecure(screenshotBuffer.containsSecureLayers())
+ .setOpaque(true)
+ .setCallsite("ShellRotationAnimation")
+ .setName("RotationLayer")
+ .build();
+
+ final ColorSpace colorSpace = screenshotBuffer.getColorSpace();
+ final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+ t.setDataSpace(mScreenshotLayer, colorSpace.getDataSpace());
+ t.setBuffer(mScreenshotLayer, hardwareBuffer);
+ t.show(mScreenshotLayer);
+ if (!isCustomRotate()) {
+ mStartLuma = getMedianBorderLuma(hardwareBuffer, colorSpace);
+ }
+ }
t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
t.show(mAnimLeash);
// Crop the real content in case it contains a larger child layer, e.g. wallpaper.
t.setCrop(mSurfaceControl, new Rect(0, 0, mEndWidth, mEndHeight));
- final ColorSpace colorSpace = screenshotBuffer.getColorSpace();
- final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
- t.setDataSpace(mScreenshotLayer, colorSpace.getDataSpace());
- t.setBuffer(mScreenshotLayer, hardwareBuffer);
- t.show(mScreenshotLayer);
-
if (!isCustomRotate()) {
mBackColorSurface = new SurfaceControl.Builder(session)
.setParent(rootLeash)
@@ -181,8 +190,6 @@
.setName("BackColorSurface")
.build();
- mStartLuma = getMedianBorderLuma(hardwareBuffer, colorSpace);
-
t.setLayer(mBackColorSurface, -1);
t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
t.show(mBackColorSurface);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
index 2cff171..eab75b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.util;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.os.Parcel;
@@ -24,40 +25,142 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Simple container for recent tasks. May contain either a single or pair of tasks.
*/
public class GroupedRecentTaskInfo implements Parcelable {
- public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
- public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
- public @Nullable SplitBounds mSplitBounds;
- public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
- this(task1, null, null);
+ public static final int TYPE_SINGLE = 1;
+ public static final int TYPE_SPLIT = 2;
+ public static final int TYPE_FREEFORM = 3;
+
+ @IntDef(prefix = {"TYPE_"}, value = {
+ TYPE_SINGLE,
+ TYPE_SPLIT,
+ TYPE_FREEFORM
+ })
+ public @interface GroupType {}
+
+ @NonNull
+ private final ActivityManager.RecentTaskInfo[] mTasks;
+ @Nullable
+ private final SplitBounds mSplitBounds;
+ @GroupType
+ private final int mType;
+
+ /**
+ * Create new for a single task
+ */
+ public static GroupedRecentTaskInfo forSingleTask(
+ @NonNull ActivityManager.RecentTaskInfo task) {
+ return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
+ TYPE_SINGLE);
}
- public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
- @Nullable ActivityManager.RecentTaskInfo task2,
- @Nullable SplitBounds splitBounds) {
- mTaskInfo1 = task1;
- mTaskInfo2 = task2;
+ /**
+ * Create new for a pair of tasks in split screen
+ */
+ public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1,
+ @NonNull ActivityManager.RecentTaskInfo task2, @Nullable SplitBounds splitBounds) {
+ return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task1, task2},
+ splitBounds, TYPE_SPLIT);
+ }
+
+ /**
+ * Create new for a group of freeform tasks
+ */
+ public static GroupedRecentTaskInfo forFreeformTasks(
+ @NonNull ActivityManager.RecentTaskInfo... tasks) {
+ return new GroupedRecentTaskInfo(tasks, null, TYPE_FREEFORM);
+ }
+
+ private GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @Nullable SplitBounds splitBounds, @GroupType int type) {
+ mTasks = tasks;
mSplitBounds = splitBounds;
+ mType = type;
}
GroupedRecentTaskInfo(Parcel parcel) {
- mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
- mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
+ mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
+ mType = parcel.readInt();
+ }
+
+ /**
+ * Get primary {@link ActivityManager.RecentTaskInfo}
+ */
+ @NonNull
+ public ActivityManager.RecentTaskInfo getTaskInfo1() {
+ return mTasks[0];
+ }
+
+ /**
+ * Get secondary {@link ActivityManager.RecentTaskInfo}.
+ *
+ * Used in split screen.
+ */
+ @Nullable
+ public ActivityManager.RecentTaskInfo getTaskInfo2() {
+ if (mTasks.length > 1) {
+ return mTasks[1];
+ }
+ return null;
+ }
+
+ /**
+ * Get all {@link ActivityManager.RecentTaskInfo}s grouped together.
+ */
+ public List<ActivityManager.RecentTaskInfo> getAllTaskInfos() {
+ return Arrays.asList(mTasks);
+ }
+
+ /**
+ * Return {@link SplitBounds} if this is a split screen entry or {@code null}
+ */
+ @Nullable
+ public SplitBounds getSplitBounds() {
+ return mSplitBounds;
+ }
+
+ /**
+ * Get type of this recents entry. One of {@link GroupType}
+ */
+ @GroupType
+ public int getType() {
+ return mType;
}
@Override
public String toString() {
- String taskString = "Task1: " + getTaskInfo(mTaskInfo1)
- + ", Task2: " + getTaskInfo(mTaskInfo2);
- if (mSplitBounds != null) {
- taskString += ", SplitBounds: " + mSplitBounds.toString();
+ StringBuilder taskString = new StringBuilder();
+ for (int i = 0; i < mTasks.length; i++) {
+ if (i == 0) {
+ taskString.append("Task");
+ } else {
+ taskString.append(", Task");
+ }
+ taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks[i]));
}
- return taskString;
+ if (mSplitBounds != null) {
+ taskString.append(", SplitBounds: ").append(mSplitBounds);
+ }
+ taskString.append(", Type=").append(mType);
+ switch (mType) {
+ case TYPE_SINGLE:
+ taskString.append("TYPE_SINGLE");
+ break;
+ case TYPE_SPLIT:
+ taskString.append("TYPE_SPLIT");
+ break;
+ case TYPE_FREEFORM:
+ taskString.append("TYPE_FREEFORM");
+ break;
+ }
+ return taskString.toString();
}
private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
@@ -74,9 +177,9 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeTypedObject(mTaskInfo1, flags);
- parcel.writeTypedObject(mTaskInfo2, flags);
+ parcel.writeTypedArray(mTasks, flags);
parcel.writeTypedObject(mSplitBounds, flags);
+ parcel.writeInt(mType);
}
@Override
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 8b13721..5040bc3 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
@@ -34,7 +34,7 @@
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.desktopmode.DesktopModeConstants;
+import com.android.wm.shell.desktopmode.DesktopMode;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -164,7 +164,7 @@
View caption = mResult.mRootView.findViewById(R.id.caption);
caption.setOnTouchListener(mOnCaptionTouchListener);
View maximize = caption.findViewById(R.id.maximize_window);
- if (DesktopModeConstants.IS_FEATURE_ENABLED) {
+ if (DesktopMode.IS_SUPPORTED) {
// Hide maximize button when desktop mode is available
maximize.setVisibility(View.GONE);
} else {
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 f512b0d..3d01495 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
@@ -87,7 +87,7 @@
try {
mWindowSession.grantInputChannel(
mDisplayId,
- new SurfaceControl(mDecorationSurface, TAG),
+ mDecorationSurface,
mFakeWindow,
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
@@ -150,8 +150,7 @@
mWindowSession.updateInputChannel(
mInputChannel.getToken(),
mDisplayId,
- new SurfaceControl(
- mDecorationSurface, "DragResizeInputListener#setTouchRegion"),
+ mDecorationSurface,
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
touchRegion);
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 2f41aa9..3e3a864 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
@@ -66,6 +66,7 @@
final DisplayController mDisplayController;
final ShellTaskOrganizer mTaskOrganizer;
final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
+ final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
@@ -104,8 +105,8 @@
RunningTaskInfo taskInfo,
SurfaceControl taskSurface) {
this(context, displayController, taskOrganizer, taskInfo, taskSurface,
- SurfaceControl.Builder::new, WindowContainerTransaction::new,
- new SurfaceControlViewHostFactory() {});
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {});
}
WindowDecoration(
@@ -115,6 +116,7 @@
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
mContext = context;
@@ -123,6 +125,7 @@
mTaskInfo = taskInfo;
mTaskSurface = taskSurface;
mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
+ mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
@@ -320,19 +323,28 @@
mCaptionWindowManager = null;
+ final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ boolean released = false;
if (mCaptionContainerSurface != null) {
- mCaptionContainerSurface.release();
+ t.remove(mCaptionContainerSurface);
mCaptionContainerSurface = null;
+ released = true;
}
if (mDecorationContainerSurface != null) {
- mDecorationContainerSurface.release();
+ t.remove(mDecorationContainerSurface);
mDecorationContainerSurface = null;
+ released = true;
}
if (mTaskBackgroundSurface != null) {
- mTaskBackgroundSurface.release();
+ t.remove(mTaskBackgroundSurface);
mTaskBackgroundSurface = null;
+ released = true;
+ }
+
+ if (released) {
+ t.apply();
}
final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 6fcd17a..b1b9736 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -130,7 +130,7 @@
testSpec.assertLayers {
val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
- current.visibleRegion.coversAtMost(previous.visibleRegion.region)
+ current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index a99439e..3bd2bf3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -46,7 +46,7 @@
/**
* Test entering pip while changing orientation (from app in landscape to pip window in portrait)
*
- * To run this test: `atest EnterPipToOtherOrientationTest:EnterPipToOtherOrientationTest`
+ * To run this test: `atest WMShellFlickerTests:EnterPipToOtherOrientationTest`
*
* Actions:
* Launch [testApp] on a fixed portrait orientation
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 102a78b..49b31d8 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@
*
* To run this test: `atest WMShellFlickerTests:CopyContentInSplit`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index bf91292..2d3a726 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -44,6 +45,7 @@
*
* To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 20a7423..df855bb 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@
*
* To run this test: `atest WMShellFlickerTests:DismissSplitScreenByGoHome`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 8f7673b..47c1ca7 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -42,6 +43,7 @@
*
* To run this test: `atest WMShellFlickerTests:DragDividerToResize`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 8e2769f..eebf5a2 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -47,6 +48,7 @@
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 531d376..d4cec3b 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -48,6 +49,7 @@
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromNotification`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index ea43c7e..28cad6d 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -47,6 +48,7 @@
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index 7ce23c5..518486f 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
@@ -42,6 +43,7 @@
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 58f7b04..8a22d55a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -42,6 +43,7 @@
*
* To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 0dd6706..80d5999 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@
*
* To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index dc8ba0c..79c917a 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@
*
* To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index e5924c5..166caa7 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@
*
* To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index b29c436..7cbace5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -16,6 +16,8 @@
package com.android.wm.shell;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+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;
@@ -637,26 +639,22 @@
}
@Test
- public void testPrepareClearBoundsForTasks() {
- RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_UNDEFINED);
- task1.displayId = 1;
+ public void testPrepareClearBoundsForStandardTasks() {
MockToken token1 = new MockToken();
- task1.token = token1.token();
+ RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_UNDEFINED, token1);
mOrganizer.onTaskAppeared(task1, null);
- RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_UNDEFINED);
- task2.displayId = 1;
MockToken token2 = new MockToken();
- task2.token = token2.token();
+ RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_UNDEFINED, token2);
mOrganizer.onTaskAppeared(task2, null);
- RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_UNDEFINED);
- otherDisplayTask.displayId = 2;
MockToken otherDisplayToken = new MockToken();
- otherDisplayTask.token = otherDisplayToken.token();
+ RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_UNDEFINED,
+ otherDisplayToken);
+ otherDisplayTask.displayId = 2;
mOrganizer.onTaskAppeared(otherDisplayTask, null);
- WindowContainerTransaction wct = mOrganizer.prepareClearBoundsForTasks(1);
+ WindowContainerTransaction wct = mOrganizer.prepareClearBoundsForStandardTasks(1);
assertEquals(wct.getChanges().size(), 2);
Change boundsChange1 = wct.getChanges().get(token1.binder());
@@ -673,26 +671,40 @@
}
@Test
- public void testPrepareClearFreeformForTasks() {
- RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FREEFORM);
- task1.displayId = 1;
+ public void testPrepareClearBoundsForStandardTasks_onlyClearActivityTypeStandard() {
MockToken token1 = new MockToken();
- task1.token = token1.token();
+ RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_UNDEFINED, token1);
mOrganizer.onTaskAppeared(task1, null);
- RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
- task2.displayId = 1;
MockToken token2 = new MockToken();
- task2.token = token2.token();
+ RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_UNDEFINED, token2);
+ task2.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME);
mOrganizer.onTaskAppeared(task2, null);
- RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_FREEFORM);
- otherDisplayTask.displayId = 2;
+ WindowContainerTransaction wct = mOrganizer.prepareClearBoundsForStandardTasks(1);
+
+ // Only clear bounds for task1
+ assertEquals(1, wct.getChanges().size());
+ assertNotNull(wct.getChanges().get(token1.binder()));
+ }
+
+ @Test
+ public void testPrepareClearFreeformForStandardTasks() {
+ MockToken token1 = new MockToken();
+ RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FREEFORM, token1);
+ mOrganizer.onTaskAppeared(task1, null);
+
+ MockToken token2 = new MockToken();
+ RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW, token2);
+ mOrganizer.onTaskAppeared(task2, null);
+
MockToken otherDisplayToken = new MockToken();
- otherDisplayTask.token = otherDisplayToken.token();
+ RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_FREEFORM,
+ otherDisplayToken);
+ otherDisplayTask.displayId = 2;
mOrganizer.onTaskAppeared(otherDisplayTask, null);
- WindowContainerTransaction wct = mOrganizer.prepareClearFreeformForTasks(1);
+ WindowContainerTransaction wct = mOrganizer.prepareClearFreeformForStandardTasks(1);
// Only task with freeform windowing mode and the right display should be updated
assertEquals(wct.getChanges().size(), 1);
@@ -701,6 +713,24 @@
assertEquals(wmModeChange1.getWindowingMode(), WINDOWING_MODE_UNDEFINED);
}
+ @Test
+ public void testPrepareClearFreeformForStandardTasks_onlyClearActivityTypeStandard() {
+ MockToken token1 = new MockToken();
+ RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FREEFORM, token1);
+ mOrganizer.onTaskAppeared(task1, null);
+
+ MockToken token2 = new MockToken();
+ RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_FREEFORM, token2);
+ task2.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME);
+ mOrganizer.onTaskAppeared(task2, null);
+
+ WindowContainerTransaction wct = mOrganizer.prepareClearFreeformForStandardTasks(1);
+
+ // Only clear freeform for task1
+ assertEquals(1, wct.getChanges().size());
+ assertNotNull(wct.getChanges().get(token1.binder()));
+ }
+
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
@@ -708,6 +738,14 @@
return taskInfo;
}
+ private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode, MockToken token) {
+ RunningTaskInfo taskInfo = createTaskInfo(taskId, windowingMode);
+ taskInfo.displayId = 1;
+ taskInfo.token = token.token();
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ return taskInfo;
+ }
+
private static class MockToken {
private final WindowContainerToken mToken;
private final IBinder mBinder;
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 58f20da..ef532e4 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
@@ -88,8 +88,8 @@
WindowContainerTransaction taskWct = new WindowContainerTransaction();
MockToken taskMockToken = new MockToken();
taskWct.setWindowingMode(taskMockToken.token(), WINDOWING_MODE_UNDEFINED);
- when(mShellTaskOrganizer.prepareClearFreeformForTasks(mContext.getDisplayId())).thenReturn(
- taskWct);
+ when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(
+ mContext.getDisplayId())).thenReturn(taskWct);
// Create a fake WCT to simulate setting display windowing mode to freeform
WindowContainerTransaction displayWct = new WindowContainerTransaction();
@@ -99,7 +99,7 @@
WINDOWING_MODE_FREEFORM)).thenReturn(displayWct);
// The test
- mController.updateDesktopModeEnabled(true);
+ mController.updateDesktopModeActive(true);
ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
WindowContainerTransaction.class);
@@ -126,15 +126,15 @@
WindowContainerTransaction taskWmWct = new WindowContainerTransaction();
MockToken taskWmMockToken = new MockToken();
taskWmWct.setWindowingMode(taskWmMockToken.token(), WINDOWING_MODE_UNDEFINED);
- when(mShellTaskOrganizer.prepareClearFreeformForTasks(mContext.getDisplayId())).thenReturn(
- taskWmWct);
+ when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(
+ mContext.getDisplayId())).thenReturn(taskWmWct);
// Create a fake WCT to simulate clearing task bounds
WindowContainerTransaction taskBoundsWct = new WindowContainerTransaction();
MockToken taskBoundsMockToken = new MockToken();
taskBoundsWct.setBounds(taskBoundsMockToken.token(), null);
- when(mShellTaskOrganizer.prepareClearBoundsForTasks(mContext.getDisplayId())).thenReturn(
- taskBoundsWct);
+ when(mShellTaskOrganizer.prepareClearBoundsForStandardTasks(
+ mContext.getDisplayId())).thenReturn(taskBoundsWct);
// Create a fake WCT to simulate setting display windowing mode to fullscreen
WindowContainerTransaction displayWct = new WindowContainerTransaction();
@@ -144,7 +144,7 @@
WINDOWING_MODE_FULLSCREEN)).thenReturn(displayWct);
// The test
- mController.updateDesktopModeEnabled(false);
+ mController.updateDesktopModeActive(false);
ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
WindowContainerTransaction.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 0059846..262e429 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -64,7 +64,7 @@
initializeMockResources();
mPipBoundsState = new PipBoundsState(mContext);
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
- new PipSnapAlgorithm());
+ new PipSnapAlgorithm(), new PipKeepClearAlgorithm() {});
mPipBoundsState.setDisplayLayout(
new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 579638d..9088077 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -98,7 +98,7 @@
mPipBoundsState = new PipBoundsState(mContext);
mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
- new PipSnapAlgorithm());
+ new PipSnapAlgorithm(), new PipKeepClearAlgorithm() {});
mMainExecutor = new TestShellExecutor();
mPipTaskOrganizer = new PipTaskOrganizer(mContext,
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
similarity index 64%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
index e0f7e35..4d7e9e4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
@@ -34,61 +34,61 @@
import java.util.Set;
/**
- * Unit tests against {@link PipKeepClearAlgorithm}.
+ * Unit tests against {@link PhonePipKeepClearAlgorithm}.
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class PipKeepClearAlgorithmTest extends ShellTestCase {
+public class PhonePipKeepClearAlgorithmTest extends ShellTestCase {
- private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
+ private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm;
private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000);
@Before
public void setUp() throws Exception {
- mPipKeepClearAlgorithm = new PipKeepClearAlgorithm();
+ mPipKeepClearAlgorithm = new PhonePipKeepClearAlgorithm(mContext);
}
@Test
- public void adjust_withCollidingRestrictedKeepClearAreas_movesBounds() {
+ public void findUnoccludedPosition_withCollidingRestrictedKeepClearArea_movesBounds() {
final Rect inBounds = new Rect(0, 0, 100, 100);
final Rect keepClearRect = new Rect(50, 50, 150, 150);
- final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
- Set.of(), DISPLAY_BOUNDS);
+ final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds,
+ Set.of(keepClearRect), Set.of(), DISPLAY_BOUNDS);
assertFalse(outBounds.contains(keepClearRect));
}
@Test
- public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() {
+ public void findUnoccludedPosition_withNonCollidingRestrictedKeepClearArea_boundsUnchanged() {
final Rect inBounds = new Rect(0, 0, 100, 100);
final Rect keepClearRect = new Rect(100, 100, 150, 150);
- final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
- Set.of(), DISPLAY_BOUNDS);
+ final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds,
+ Set.of(keepClearRect), Set.of(), DISPLAY_BOUNDS);
assertEquals(inBounds, outBounds);
}
@Test
- public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
+ public void findUnoccludedPosition_withCollidingUnrestrictedKeepClearArea_moveBounds() {
// TODO(b/183746978): update this test to accommodate for the updated algorithm
final Rect inBounds = new Rect(0, 0, 100, 100);
final Rect keepClearRect = new Rect(50, 50, 150, 150);
- final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(),
+ final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds, Set.of(),
Set.of(keepClearRect), DISPLAY_BOUNDS);
- assertEquals(inBounds, outBounds);
+ assertFalse(outBounds.contains(keepClearRect));
}
@Test
- public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
+ public void findUnoccludedPosition_withNonCollidingUnrestrictedKeepClearArea_boundsUnchanged() {
final Rect inBounds = new Rect(0, 0, 100, 100);
final Rect keepClearRect = new Rect(100, 100, 150, 150);
- final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(),
+ final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds, Set.of(),
Set.of(keepClearRect), DISPLAY_BOUNDS);
assertEquals(inBounds, outBounds);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index eb5726b..1b5091f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -64,6 +64,7 @@
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;
@@ -85,7 +86,7 @@
@Mock private PhonePipMenuController mMockPhonePipMenuController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
- @Mock private PipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
+ @Mock private PhonePipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
@Mock private PipSnapAlgorithm mMockPipSnapAlgorithm;
@Mock private PipMediaController mMockPipMediaController;
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
@@ -267,7 +268,20 @@
}
@Test
- public void onKeepClearAreasChanged_updatesPipBoundsState() {
+ public void onKeepClearAreasChanged_featureDisabled_pipBoundsStateDoesntChange() {
+ final int displayId = 1;
+ final Rect keepClearArea = new Rect(0, 0, 10, 10);
+ when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
+
+ mPipController.mDisplaysChangedListener.onKeepClearAreasChanged(
+ displayId, Set.of(keepClearArea), Set.of());
+
+ verify(mMockPipBoundsState, never()).setKeepClearAreas(Mockito.anySet(), Mockito.anySet());
+ }
+
+ @Test
+ public void onKeepClearAreasChanged_featureEnabled_updatesPipBoundsState() {
+ mPipController.setEnablePipKeepClearAlgorithm(true);
final int displayId = 1;
final Rect keepClearArea = new Rect(0, 0, 10, 10);
when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index dd10aa7..dba037d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -36,6 +36,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipKeepClearAlgorithm;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -87,8 +88,10 @@
MockitoAnnotations.initMocks(this);
mPipBoundsState = new PipBoundsState(mContext);
final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
+ final PipKeepClearAlgorithm pipKeepClearAlgorithm =
+ new PipKeepClearAlgorithm() {};
final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
- mPipBoundsState, pipSnapAlgorithm);
+ mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm);
final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
mMockPipTransitionController, mFloatingContentCoordinator);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index ecefd89..474d6aa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -34,6 +34,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipKeepClearAlgorithm;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -104,7 +105,8 @@
MockitoAnnotations.initMocks(this);
mPipBoundsState = new PipBoundsState(mContext);
mPipSnapAlgorithm = new PipSnapAlgorithm();
- mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm);
+ mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
+ new PipKeepClearAlgorithm() {});
PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
mMockPipTransitionController, mFloatingContentCoordinator);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 81bb609..9e755dc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -20,6 +20,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -45,11 +48,13 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -179,6 +184,46 @@
}
@Test
+ public void testGetRecentTasks_groupActiveFreeformTasks() {
+ StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
+ DesktopMode.class).startMocking();
+ when(DesktopMode.isActive(any())).thenReturn(true);
+
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ setRawList(t1, t2, t3, t4);
+
+ mRecentTasksController.addActiveFreeformTask(1);
+ mRecentTasksController.addActiveFreeformTask(3);
+
+ ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
+ MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+
+ // 2 freeform tasks should be grouped into one, 3 total recents entries
+ assertEquals(3, recentTasks.size());
+ GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
+ GroupedRecentTaskInfo singleGroup2 = recentTasks.get(2);
+
+ // Check that groups have expected types
+ assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup1.getType());
+ assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
+
+ // Check freeform group entries
+ assertEquals(t1, freeformGroup.getAllTaskInfos().get(0));
+ assertEquals(t3, freeformGroup.getAllTaskInfos().get(1));
+
+ // Check single entries
+ assertEquals(t2, singleGroup1.getTaskInfo1());
+ assertEquals(t4, singleGroup2.getTaskInfo1());
+
+ mockitoSession.finishMocking();
+ }
+
+ @Test
public void testRemovedTaskRemovesSplit() {
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -254,6 +299,7 @@
/**
* Asserts that the recent tasks matches the given task ids.
+ *
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list
*/
@@ -262,22 +308,23 @@
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
GroupedRecentTaskInfo pair = recentTasks.get(i);
- int taskId1 = pair.mTaskInfo1.taskId;
+ int taskId1 = pair.getTaskInfo1().taskId;
flattenedTaskIds[2 * i] = taskId1;
- flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null
- ? pair.mTaskInfo2.taskId
+ flattenedTaskIds[2 * i + 1] = pair.getTaskInfo2() != null
+ ? pair.getTaskInfo2().taskId
: -1;
- if (pair.mTaskInfo2 != null) {
- assertNotNull(pair.mSplitBounds);
- int leftTopTaskId = pair.mSplitBounds.leftTopTaskId;
- int bottomRightTaskId = pair.mSplitBounds.rightBottomTaskId;
+ if (pair.getTaskInfo2() != null) {
+ assertNotNull(pair.getSplitBounds());
+ int leftTopTaskId = pair.getSplitBounds().leftTopTaskId;
+ int bottomRightTaskId = pair.getSplitBounds().rightBottomTaskId;
// Unclear if pairs are ordered by split position, most likely not.
- assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId);
+ assertTrue(leftTopTaskId == taskId1
+ || leftTopTaskId == pair.getTaskInfo2().taskId);
assertTrue(bottomRightTaskId == taskId1
- || bottomRightTaskId == pair.mTaskInfo2.taskId);
+ || bottomRightTaskId == pair.getTaskInfo2().taskId);
} else {
- assertNull(pair.mSplitBounds);
+ assertNull(pair.getSplitBounds());
}
}
assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
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 1b74d7d..ab6ac94 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
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
@@ -59,6 +60,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import java.util.ArrayList;
@@ -96,6 +98,8 @@
@Mock
private WindowContainerTransaction mMockWindowContainerTransaction;
+ private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions =
+ new ArrayList<>();
private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>();
private SurfaceControl.Transaction mMockSurfaceControlStartT;
private SurfaceControl.Transaction mMockSurfaceControlFinishT;
@@ -265,6 +269,9 @@
createMockSurfaceControlBuilder(captionContainerSurface);
mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ mMockSurfaceControlTransactions.add(t);
+
final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
new ActivityManager.TaskDescription.Builder()
.setBackgroundColor(Color.YELLOW);
@@ -287,19 +294,19 @@
windowDecor.relayout(taskInfo);
verify(mMockSurfaceControlViewHost, never()).release();
- verify(decorContainerSurface, never()).release();
- verify(taskBackgroundSurface, never()).release();
- verify(captionContainerSurface, never()).release();
+ verify(t, never()).apply();
verify(mMockWindowContainerTransaction, never())
.removeInsetsProvider(eq(taskInfo.token), any());
taskInfo.isVisible = false;
windowDecor.relayout(taskInfo);
- verify(mMockSurfaceControlViewHost).release();
- verify(decorContainerSurface).release();
- verify(taskBackgroundSurface).release();
- verify(captionContainerSurface).release();
+ final InOrder releaseOrder = inOrder(t, mMockSurfaceControlViewHost);
+ releaseOrder.verify(mMockSurfaceControlViewHost).release();
+ releaseOrder.verify(t).remove(captionContainerSurface);
+ releaseOrder.verify(t).remove(decorContainerSurface);
+ releaseOrder.verify(t).remove(taskBackgroundSurface);
+ releaseOrder.verify(t).apply();
verify(mMockWindowContainerTransaction).removeInsetsProvider(eq(taskInfo.token), any());
}
@@ -351,21 +358,30 @@
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
- taskInfo, testSurface, new MockSurfaceControlBuilderSupplier(),
+ taskInfo, testSurface,
+ new MockObjectSupplier<>(mMockSurfaceControlBuilders,
+ () -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))),
+ new MockObjectSupplier<>(mMockSurfaceControlTransactions,
+ () -> mock(SurfaceControl.Transaction.class)),
() -> mMockWindowContainerTransaction, mMockSurfaceControlViewHostFactory);
}
- private class MockSurfaceControlBuilderSupplier implements Supplier<SurfaceControl.Builder> {
+ private class MockObjectSupplier<T> implements Supplier<T> {
+ private final List<T> mObjects;
+ private final Supplier<T> mDefaultSupplier;
private int mNumOfCalls = 0;
+ private MockObjectSupplier(List<T> objects, Supplier<T> defaultSupplier) {
+ mObjects = objects;
+ mDefaultSupplier = defaultSupplier;
+ }
+
@Override
- public SurfaceControl.Builder get() {
- final SurfaceControl.Builder builder =
- mNumOfCalls < mMockSurfaceControlBuilders.size()
- ? mMockSurfaceControlBuilders.get(mNumOfCalls)
- : createMockSurfaceControlBuilder(mock(SurfaceControl.class));
+ public T get() {
+ final T mock = mNumOfCalls < mObjects.size()
+ ? mObjects.get(mNumOfCalls) : mDefaultSupplier.get();
++mNumOfCalls;
- return builder;
+ return mock;
}
}
@@ -383,11 +399,12 @@
ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
super(context, displayController, taskOrganizer, taskInfo, taskSurface,
- surfaceControlBuilderSupplier, windowContainerTransactionSupplier,
- surfaceControlViewHostFactory);
+ surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ windowContainerTransactionSupplier, surfaceControlViewHostFactory);
}
@Override
diff --git a/libs/dream/lowlight/Android.bp b/libs/dream/lowlight/Android.bp
new file mode 100644
index 0000000..5b5b0f0
--- /dev/null
+++ b/libs/dream/lowlight/Android.bp
@@ -0,0 +1,47 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "low_light_dream_lib-sources",
+ srcs: [
+ "src/**/*.java",
+ ],
+ path: "src",
+}
+
+android_library {
+ name: "LowLightDreamLib",
+ srcs: [
+ ":low_light_dream_lib-sources",
+ ],
+ resource_dirs: [
+ "res",
+ ],
+ static_libs: [
+ "androidx.arch.core_core-runtime",
+ "dagger2",
+ "jsr330",
+ ],
+ manifest: "AndroidManifest.xml",
+ plugins: ["dagger2-compiler"],
+}
diff --git a/libs/dream/lowlight/AndroidManifest.xml b/libs/dream/lowlight/AndroidManifest.xml
new file mode 100644
index 0000000..a8d9526
--- /dev/null
+++ b/libs/dream/lowlight/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest package="com.android.dream.lowlight" />
diff --git a/libs/dream/lowlight/res/values/config.xml b/libs/dream/lowlight/res/values/config.xml
new file mode 100644
index 0000000..70fe073
--- /dev/null
+++ b/libs/dream/lowlight/res/values/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <!-- The dream component used when the device is low light environment. -->
+ <string translatable="false" name="config_lowLightDreamComponent"/>
+</resources>
diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java
new file mode 100644
index 0000000..5ecec4d
--- /dev/null
+++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java
@@ -0,0 +1,117 @@
+/*
+ * 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.dream.lowlight;
+
+import static com.android.dream.lowlight.dagger.LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT;
+
+import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
+import android.app.DreamManager;
+import android.content.ComponentName;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Maintains the ambient light mode of the environment the device is in, and sets a low light dream
+ * component, if present, as the system dream when the ambient light mode is low light.
+ *
+ * @hide
+ */
+public final class LowLightDreamManager {
+ private static final String TAG = "LowLightDreamManager";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "AMBIENT_LIGHT_MODE_" }, value = {
+ AMBIENT_LIGHT_MODE_UNKNOWN,
+ AMBIENT_LIGHT_MODE_REGULAR,
+ AMBIENT_LIGHT_MODE_LOW_LIGHT
+ })
+ public @interface AmbientLightMode {}
+
+ /**
+ * Constant for ambient light mode being unknown.
+ * @hide
+ */
+ public static final int AMBIENT_LIGHT_MODE_UNKNOWN = 0;
+
+ /**
+ * Constant for ambient light mode being regular / bright.
+ * @hide
+ */
+ public static final int AMBIENT_LIGHT_MODE_REGULAR = 1;
+
+ /**
+ * Constant for ambient light mode being low light / dim.
+ * @hide
+ */
+ public static final int AMBIENT_LIGHT_MODE_LOW_LIGHT = 2;
+
+ private final DreamManager mDreamManager;
+
+ @Nullable
+ private final ComponentName mLowLightDreamComponent;
+
+ private int mAmbientLightMode = AMBIENT_LIGHT_MODE_UNKNOWN;
+
+ @Inject
+ public LowLightDreamManager(
+ DreamManager dreamManager,
+ @Named(LOW_LIGHT_DREAM_COMPONENT) @Nullable ComponentName lowLightDreamComponent) {
+ mDreamManager = dreamManager;
+ mLowLightDreamComponent = lowLightDreamComponent;
+ }
+
+ /**
+ * Sets the current ambient light mode.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ public void setAmbientLightMode(@AmbientLightMode int ambientLightMode) {
+ if (mLowLightDreamComponent == null) {
+ if (DEBUG) {
+ Log.d(TAG, "ignore ambient light mode change because low light dream component "
+ + "is empty");
+ }
+ return;
+ }
+
+ if (mAmbientLightMode == ambientLightMode) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "ambient light mode changed from " + mAmbientLightMode + " to "
+ + ambientLightMode);
+ }
+
+ mAmbientLightMode = ambientLightMode;
+
+ mDreamManager.setSystemDreamComponent(mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT
+ ? mLowLightDreamComponent : null);
+ }
+}
diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/dagger/LowLightDreamModule.java b/libs/dream/lowlight/src/com/android/dream/lowlight/dagger/LowLightDreamModule.java
new file mode 100644
index 0000000..c183a04
--- /dev/null
+++ b/libs/dream/lowlight/src/com/android/dream/lowlight/dagger/LowLightDreamModule.java
@@ -0,0 +1,61 @@
+/*
+ * 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.dream.lowlight.dagger;
+
+import android.app.DreamManager;
+import android.content.ComponentName;
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.dream.lowlight.R;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger module for low light dream.
+ *
+ * @hide
+ */
+@Module
+public interface LowLightDreamModule {
+ String LOW_LIGHT_DREAM_COMPONENT = "low_light_dream_component";
+
+ /**
+ * Provides dream manager.
+ */
+ @Provides
+ static DreamManager providesDreamManager(Context context) {
+ return context.getSystemService(DreamManager.class);
+ }
+
+ /**
+ * Provides the component name of the low light dream, or null if not configured.
+ */
+ @Provides
+ @Named(LOW_LIGHT_DREAM_COMPONENT)
+ @Nullable
+ static ComponentName providesLowLightDreamComponent(Context context) {
+ final String lowLightDreamComponent = context.getResources().getString(
+ R.string.config_lowLightDreamComponent);
+ return lowLightDreamComponent.isEmpty() ? null
+ : ComponentName.unflattenFromString(lowLightDreamComponent);
+ }
+}
diff --git a/libs/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp
new file mode 100644
index 0000000..bd6f05e
--- /dev/null
+++ b/libs/dream/lowlight/tests/Android.bp
@@ -0,0 +1,45 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "LowLightDreamTests",
+ srcs: [
+ "**/*.java",
+ ],
+ static_libs: [
+ "LowLightDreamLib",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "frameworks-base-testutils",
+ "junit",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "testables",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+}
diff --git a/libs/dream/lowlight/tests/AndroidManifest.xml b/libs/dream/lowlight/tests/AndroidManifest.xml
new file mode 100644
index 0000000..abb71fb
--- /dev/null
+++ b/libs/dream/lowlight/tests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.dream.lowlight.tests">
+
+ <application android:debuggable="true" android:largeHeap="true">
+ <uses-library android:name="android.test.mock" />
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Tests for LowLightDreamLib"
+ android:targetPackage="com.android.dream.lowlight.tests">
+ </instrumentation>
+
+</manifest>
diff --git a/libs/dream/lowlight/tests/AndroidTest.xml b/libs/dream/lowlight/tests/AndroidTest.xml
new file mode 100644
index 0000000..1080033
--- /dev/null
+++ b/libs/dream/lowlight/tests/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Tests for LowLightDreamLib">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="LowLightDreamTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="LowLightDreamLibTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.dream.lowlight.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java
new file mode 100644
index 0000000..91a170f
--- /dev/null
+++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.dream.lowlight;
+
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT;
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR;
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_UNKNOWN;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.DreamManager;
+import android.content.ComponentName;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+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)
+public class LowLightDreamManagerTest {
+ @Mock
+ private DreamManager mDreamManager;
+
+ @Mock
+ private ComponentName mDreamComponent;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void setAmbientLightMode_lowLight_setSystemDream() {
+ final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
+ mDreamComponent);
+
+ lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+
+ verify(mDreamManager).setSystemDreamComponent(mDreamComponent);
+ }
+
+ @Test
+ public void setAmbientLightMode_regularLight_clearSystemDream() {
+ final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
+ mDreamComponent);
+
+ lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
+
+ verify(mDreamManager).setSystemDreamComponent(null);
+ }
+
+ @Test
+ public void setAmbientLightMode_defaultUnknownMode_clearSystemDream() {
+ final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
+ mDreamComponent);
+
+ // Set to low light first.
+ lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+ clearInvocations(mDreamManager);
+
+ // Return to default unknown mode.
+ lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);
+
+ verify(mDreamManager).setSystemDreamComponent(null);
+ }
+
+ @Test
+ public void setAmbientLightMode_dreamComponentNotSet_doNothing() {
+ final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
+ null /*dream component*/);
+
+ lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+
+ verify(mDreamManager, never()).setSystemDreamComponent(any());
+ }
+}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 397975d..473afbd 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -18,6 +18,7 @@
#include "CanvasProperty.h"
#include "NinePatchUtils.h"
+#include "SkBlendMode.h"
#include "VectorDrawable.h"
#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
@@ -251,10 +252,11 @@
return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
}
-void SkiaCanvas::punchHole(const SkRRect& rect) {
+void SkiaCanvas::punchHole(const SkRRect& rect, float alpha) {
SkPaint paint = SkPaint();
- paint.setColor(0);
- paint.setBlendMode(SkBlendMode::kClear);
+ paint.setColor(SkColors::kBlack);
+ paint.setAlphaf(alpha);
+ paint.setBlendMode(SkBlendMode::kDstOut);
mCanvas->drawRRect(rect, paint);
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index c6313f6..51007c5 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -65,7 +65,7 @@
LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ");
}
- virtual void punchHole(const SkRRect& rect) override;
+ virtual void punchHole(const SkRRect& rect, float alpha) override;
virtual void setBitmap(const SkBitmap& bitmap) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 7378351..82d23b5 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -152,7 +152,7 @@
LOG_ALWAYS_FATAL("Not supported");
}
- virtual void punchHole(const SkRRect& rect) = 0;
+ virtual void punchHole(const SkRRect& rect, float alpha) = 0;
// ----------------------------------------------------------------------------
// Canvas state operations
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index fb7d5f7..0513447 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -713,9 +713,10 @@
}
static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right,
- jfloat bottom, jfloat rx, jfloat ry) {
+ jfloat bottom, jfloat rx, jfloat ry, jfloat alpha) {
auto canvas = reinterpret_cast<Canvas*>(canvasPtr);
- canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry));
+ canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry),
+ alpha);
}
}; // namespace CanvasJNI
@@ -790,7 +791,7 @@
{"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
{"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
{"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
- {"nPunchHole", "(JFFFFFF)V", (void*) CanvasJNI::punchHole}
+ {"nPunchHole", "(JFFFFFFF)V", (void*) CanvasJNI::punchHole}
};
int register_android_graphics_Canvas(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 3bf2b2e..f2282e66 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -201,6 +201,7 @@
paint.setAlpha((uint8_t)paint.getAlpha() * mAlpha);
return true;
}
+
void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
// We unroll the drawable using "this" canvas, so that draw calls contained inside will
// get their alpha applied. The default SkPaintFilterCanvas::onDrawDrawable does not unroll.
@@ -292,7 +293,7 @@
// with the same canvas transformation + clip into the target
// canvas then draw the layer on top
if (renderNode->hasHolePunches()) {
- TransformCanvas transformCanvas(canvas, SkBlendMode::kClear);
+ TransformCanvas transformCanvas(canvas, SkBlendMode::kDstOut);
displayList->draw(&transformCanvas);
}
canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds),
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 5c6117d..1f87865 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -69,20 +69,22 @@
mDisplayList->setHasHolePunches(false);
}
-void SkiaRecordingCanvas::punchHole(const SkRRect& rect) {
- // Add the marker annotation to allow HWUI to determine where the current
- // clip/transformation should be applied
+void SkiaRecordingCanvas::punchHole(const SkRRect& rect, float alpha) {
+ // Add the marker annotation to allow HWUI to determine the current
+ // clip/transformation and alpha should be applied
SkVector vector = rect.getSimpleRadii();
- float data[2];
+ float data[3];
data[0] = vector.x();
data[1] = vector.y();
+ data[2] = alpha;
mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
- SkData::MakeWithCopy(data, 2 * sizeof(float)));
+ SkData::MakeWithCopy(data, sizeof(data)));
// Clear the current rect within the layer itself
SkPaint paint = SkPaint();
- paint.setColor(0);
- paint.setBlendMode(SkBlendMode::kClear);
+ paint.setColor(SkColors::kBlack);
+ paint.setAlphaf(alpha);
+ paint.setBlendMode(SkBlendMode::kDstOut);
mRecorder.drawRRect(rect, paint);
mDisplayList->setHasHolePunches(true);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 89e3a2c..7844e2c 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -50,7 +50,7 @@
initDisplayList(renderNode, width, height);
}
- virtual void punchHole(const SkRRect& rect) override;
+ virtual void punchHole(const SkRRect& rect, float alpha) override;
virtual void finishRecording(uirenderer::RenderNode* destination) override;
std::unique_ptr<SkiaDisplayList> finishRecording();
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 33160d0..c320df0 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -29,13 +29,15 @@
void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) {
if (HOLE_PUNCH_ANNOTATION == key) {
auto* rectParams = reinterpret_cast<const float*>(value->data());
- float radiusX = rectParams[0];
- float radiusY = rectParams[1];
+ const float radiusX = rectParams[0];
+ const float radiusY = rectParams[1];
+ const float alpha = rectParams[2];
SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
SkPaint paint;
paint.setColor(SkColors::kBlack);
paint.setBlendMode(mHolePunchBlendMode);
+ paint.setAlphaf(alpha);
mWrappedCanvas->drawRRect(roundRect, paint);
}
}
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index 59230a7..7d3ca96 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -138,7 +138,7 @@
roundRectPaint.setColor(Color::White);
if (addHolePunch) {
// Punch a hole but then cover it up, we don't want to actually see it
- canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)));
+ canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)), 1.f);
}
canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint);
@@ -235,4 +235,4 @@
StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
bool haveHolePunch() override { return true; }
bool forceLayer() override { return true; }
-};
\ No newline at end of file
+};
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 2b9f159..42b72d4 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -146,4 +146,5 @@
// used by gts tests to verify whitelists
String[] getBackgroundThrottlingWhitelist();
PackageTagsList getIgnoreSettingsAllowlist();
+ PackageTagsList getAdasAllowlist();
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index f596502..32015b8 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -504,6 +504,19 @@
}
/**
+ * Returns ADAS packages and their associated attribution tags.
+ *
+ * @hide
+ */
+ public @NonNull PackageTagsList getAdasAllowlist() {
+ try {
+ return mService.getAdasAllowlist();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the extra location controller package on the device.
*
* @hide
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 4f9c6f1..4cccf89 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -19,6 +19,7 @@
static_libs: [
"androidx.test.core",
+ "androidx.test.ext.truth",
"androidx.test.rules",
"compatibility-device-util-axt",
"mockito-target-minus-junit4",
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 37c8367..810b408 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -22,6 +22,8 @@
import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
+import static androidx.test.ext.truth.os.BundleSubject.assertThat;
+
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SPECIAL;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID1;
@@ -34,12 +36,10 @@
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.VOLUME_MAX;
-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 com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
import android.Manifest;
import android.app.UiAutomation;
@@ -206,9 +206,8 @@
});
mService.addRoutes(routes);
- assertTrue(
- "Added routes not found or onRoutesUpdated() never called.",
- addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Added routes not found or onRoutesUpdated() never called.")
+ .that(addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
MediaRoute2Info newRoute2 =
new MediaRoute2Info.Builder(routes.get(1))
@@ -216,18 +215,16 @@
.build();
routes.set(1, newRoute2);
mService.addRoute(routes.get(1));
- assertTrue(
- "Modified route not found or onRoutesUpdated() never called.",
- changedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Modified route not found or onRoutesUpdated() never called.")
+ .that(changedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<String> routeIds = new ArrayList<>();
routeIds.add(routeId0);
routeIds.add(routeId1);
mService.removeRoutes(routeIds);
- assertTrue(
- "Removed routes not found or onRoutesUpdated() never called.",
- removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Removed routes not found or onRoutesUpdated() never called.")
+ .that(removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
private static boolean checkRoutesMatch(
@@ -242,9 +239,10 @@
if (matchingRoute == null) {
return false;
}
- assertTrue(TextUtils.equals(expectedRoute.getName(), matchingRoute.getName()));
- assertEquals(expectedRoute.getFeatures(), matchingRoute.getFeatures());
- assertEquals(expectedRoute.getConnectionState(), matchingRoute.getConnectionState());
+ assertThat(TextUtils.equals(expectedRoute.getName(), matchingRoute.getName())).isTrue();
+ assertThat(matchingRoute.getFeatures()).isEqualTo(expectedRoute.getFeatures());
+ assertThat(matchingRoute.getConnectionState())
+ .isEqualTo(expectedRoute.getConnectionState());
}
return true;
@@ -288,19 +286,19 @@
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
MediaRoute2Info routeToRemove = routes.get(ROUTE_ID2);
- assertNotNull(routeToRemove);
+ assertThat(routeToRemove).isNotNull();
mService.removeRoute(ROUTE_ID2);
// Wait until the route is removed.
- assertTrue(removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
Map<String, MediaRoute2Info> newRoutes = waitAndGetRoutesWithManager(FEATURES_ALL);
- assertNull(newRoutes.get(ROUTE_ID2));
+ assertThat(newRoutes.get(ROUTE_ID2)).isNull();
// Revert the removal.
mService.addRoute(routeToRemove);
- assertTrue(addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
mRouter2.unregisterRouteCallback(routeCallback);
}
@@ -318,8 +316,8 @@
}
}
- assertEquals(1, routeCount);
- assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
+ assertThat(routeCount).isEqualTo(1);
+ assertThat(routes.get(ROUTE_ID_SPECIAL_FEATURE)).isNotNull();
}
@Test
@@ -337,7 +335,7 @@
}
}
- assertEquals(0, remoteRouteCount);
+ assertThat(remoteRouteCount).isEqualTo(0);
}
@Test
@@ -355,7 +353,7 @@
}
}
- assertTrue(remoteRouteCount > 0);
+ assertThat(remoteRouteCount).isGreaterThan(0);
}
/**
@@ -384,11 +382,11 @@
});
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
- assertNotNull(routeToSelect);
+ assertThat(routeToSelect).isNotNull();
mManager.selectRoute(mPackageName, routeToSelect);
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(1, mManager.getRemoteSessions().size());
+ assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+ assertThat(mManager.getRemoteSessions()).hasSize(1);
}
@Test
@@ -410,20 +408,20 @@
}
});
- assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
mManager.selectRoute(mPackageName, routeToSelect);
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
- assertEquals(2, sessions.size());
+ assertThat(sessions).hasSize(2);
RoutingSessionInfo sessionInfo = sessions.get(1);
awaitOnRouteChangedManager(
() -> mManager.releaseSession(sessionInfo),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), null));
- assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
}
@Test
@@ -437,7 +435,7 @@
@Override
public void onTransferred(RoutingSessionInfo oldSessionInfo,
RoutingSessionInfo newSessionInfo) {
- assertNotNull(newSessionInfo);
+ assertThat(newSessionInfo).isNotNull();
onSessionCreatedLatch.countDown();
}
@Override
@@ -452,8 +450,8 @@
.build();
mManager.transfer(mManager.getSystemRoutingSession(null), unknownRoute);
- assertFalse(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- assertTrue(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
+ assertThat(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
@Test
@@ -463,7 +461,7 @@
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
- assertNotNull(routeToSelect);
+ assertThat(routeToSelect).isNotNull();
addRouterCallback(new RouteCallback() {});
addManagerCallback(new MediaRouter2Manager.Callback() {
@@ -481,20 +479,20 @@
}
});
- assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
- assertEquals(1, mRouter2.getControllers().size());
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
+ assertThat(mRouter2.getControllers()).hasSize(1);
mManager.transfer(mManager.getRoutingSessions(mPackageName).get(0), routeToSelect);
- assertTrue(transferLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(transferLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- assertEquals(2, mManager.getRoutingSessions(mPackageName).size());
- assertEquals(2, mRouter2.getControllers().size());
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(2);
+ assertThat(mRouter2.getControllers()).hasSize(2);
mRouter2.getControllers().get(1).release();
- assertTrue(releaseLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(releaseLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- assertEquals(1, mRouter2.getControllers().size());
- assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
+ assertThat(mRouter2.getControllers()).hasSize(1);
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
}
/**
@@ -511,7 +509,7 @@
@Override
public void onTransferred(RoutingSessionInfo oldSessionInfo,
RoutingSessionInfo newSessionInfo) {
- assertNotNull(newSessionInfo);
+ assertThat(newSessionInfo).isNotNull();
onSessionCreatedLatch.countDown();
}
});
@@ -519,11 +517,11 @@
() -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
- assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
- assertEquals(2, sessions.size());
+ assertThat(sessions.size()).isEqualTo(2);
RoutingSessionInfo sessionInfo = sessions.get(1);
awaitOnRouteChangedManager(
@@ -582,30 +580,33 @@
MediaRoute2Info route1 = routes.get(ROUTE_ID1);
MediaRoute2Info route2 = routes.get(ROUTE_ID2);
- assertNotNull(route1);
- assertNotNull(route2);
+ assertThat(route1).isNotNull();
+ assertThat(route2).isNotNull();
mManager.selectRoute(mPackageName, route1);
- assertTrue(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
mManager.selectRoute(mPackageName, route2);
- assertTrue(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
// onTransferFailed/onSessionReleased should not be called.
- assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- assertFalse(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertThat(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
+ assertThat(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS))
+ .isFalse();
- assertEquals(2, sessions.size());
+ assertThat(sessions.size()).isEqualTo(2);
List<String> remoteSessionIds = mManager.getRemoteSessions().stream()
.map(RoutingSessionInfo::getId)
.collect(Collectors.toList());
// The old session shouldn't appear on the session list.
- assertFalse(remoteSessionIds.contains(sessions.get(0).getId()));
- assertTrue(remoteSessionIds.contains(sessions.get(1).getId()));
+ assertThat(remoteSessionIds).doesNotContain(sessions.get(0).getId());
+ assertThat(remoteSessionIds).contains(sessions.get(1).getId());
- assertFalse(serviceOnReleaseSessionLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertThat(serviceOnReleaseSessionLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS))
+ .isFalse();
mManager.releaseSession(sessions.get(0));
- assertTrue(serviceOnReleaseSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertFalse(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertThat(serviceOnReleaseSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+ assertThat(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS))
+ .isFalse();
}
@Test
@@ -633,9 +634,9 @@
RoutingSessionInfo targetSession = sessions.get(sessions.size() - 1);
mManager.transfer(targetSession, routes.get(ROUTE_ID6_TO_BE_IGNORED));
- assertFalse(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- assertTrue(onFailedLatch.await(MediaRouter2Manager.TRANSFER_TIMEOUT_MS,
- TimeUnit.MILLISECONDS));
+ assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
+ assertThat(onFailedLatch.await(MediaRouter2Manager.TRANSFER_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS)).isTrue();
}
@Test
@@ -647,7 +648,7 @@
mManager.getSystemRoutingSession(mPackageName).getSelectedRoutes().get(0));
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
MediaRoute2Info volRoute = routes.get(selectedSystemRouteId);
- assertNotNull(volRoute);
+ assertThat(volRoute).isNotNull();
int originalVolume = volRoute.getVolume();
int targetVolume = originalVolume == volRoute.getVolumeMax()
@@ -697,16 +698,16 @@
@Override
public void onTransferred(RoutingSessionInfo oldSessionInfo,
RoutingSessionInfo newSessionInfo) {
- assertNotNull(newSessionInfo);
+ assertThat(newSessionInfo).isNotNull();
onSessionCreatedLatch.countDown();
}
});
mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
- assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
- assertEquals(2, sessions.size());
+ assertThat(sessions).hasSize(2);
// test setSessionVolume
RoutingSessionInfo sessionInfo = sessions.get(1);
@@ -741,7 +742,7 @@
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mManager.setSessionVolume(sessionInfo, targetVolume);
- assertTrue(volumeChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(volumeChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
} finally {
mRouter2.unregisterControllerCallback(controllerCallback);
}
@@ -768,8 +769,8 @@
addManagerCallback(new MediaRouter2Manager.Callback() {});
mManager.setRouteVolume(volRoute, 0);
- assertTrue(onSetRouteVolumeLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertFalse(requestIds.isEmpty());
+ assertThat(onSetRouteVolumeLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+ assertThat(requestIds).isNotEmpty();
final int failureReason = REASON_REJECTED;
final CountDownLatch onRequestFailedLatch = new CountDownLatch(1);
@@ -789,16 +790,17 @@
final long invalidRequestId = REQUEST_ID_NONE;
mService.notifyRequestFailed(invalidRequestId, failureReason);
- assertFalse(onRequestFailedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertThat(onRequestFailedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
final long validRequestId = requestIds.get(0);
mService.notifyRequestFailed(validRequestId, failureReason);
- assertTrue(onRequestFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(onRequestFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
// Test calling notifyRequestFailed() multiple times with the same valid requestId.
// onRequestFailed() shouldn't be called since the requestId has been already handled.
mService.notifyRequestFailed(validRequestId, failureReason);
- assertFalse(onRequestFailedSecondCallLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(onRequestFailedSecondCallLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isFalse();
}
@Test
@@ -808,9 +810,9 @@
MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
- assertEquals(PLAYBACK_VOLUME_FIXED, fixedVolumeRoute.getVolumeHandling());
- assertEquals(PLAYBACK_VOLUME_VARIABLE, variableVolumeRoute.getVolumeHandling());
- assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
+ assertThat(fixedVolumeRoute.getVolumeHandling()).isEqualTo(PLAYBACK_VOLUME_FIXED);
+ assertThat(variableVolumeRoute.getVolumeHandling()).isEqualTo(PLAYBACK_VOLUME_VARIABLE);
+ assertThat(variableVolumeRoute.getVolumeMax()).isEqualTo(VOLUME_MAX);
}
@Test
@@ -819,7 +821,7 @@
addRouterCallback(new RouteCallback() {});
MediaRoute2Info route = routes.get(ROUTE_ID1);
- assertNotNull(route);
+ assertThat(route).isNotNull();
final Bundle controllerHints = new Bundle();
controllerHints.putString(TEST_KEY, TEST_VALUE);
@@ -837,13 +839,13 @@
@Override
public void onTransferred(RoutingSessionInfo oldSession,
RoutingSessionInfo newSession) {
- assertTrue(newSession.getSelectedRoutes().contains(route.getId()));
+ assertThat(newSession.getSelectedRoutes()).contains(route.getId());
// The StubMediaRoute2ProviderService is supposed to set control hints
// with the given controllerHints.
Bundle controlHints = newSession.getControlHints();
- assertNotNull(controlHints);
- assertTrue(controlHints.containsKey(TEST_KEY));
- assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY));
+ assertThat(controlHints).isNotNull();
+ assertThat(controlHints).containsKey(TEST_KEY);
+ assertThat(controlHints).string(TEST_KEY).isEqualTo(TEST_VALUE);
successLatch.countDown();
}
@@ -857,10 +859,10 @@
mRouter2.setOnGetControllerHintsListener(listener);
mManager.selectRoute(mPackageName, route);
- assertTrue(hintLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(hintLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+ assertThat(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertThat(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
}
@Test
@@ -885,24 +887,24 @@
@Override
public void onTransferred(RoutingSessionInfo oldSessionInfo,
RoutingSessionInfo newSessionInfo) {
- assertNotNull(newSessionInfo);
+ assertThat(newSessionInfo).isNotNull();
List<String> selectedRoutes = mManager.getSelectedRoutes(newSessionInfo).stream()
.map(MediaRoute2Info::getId)
.collect(Collectors.toList());
for (MediaRoute2Info selectableRoute :
mManager.getSelectableRoutes(newSessionInfo)) {
- assertFalse(selectedRoutes.contains(selectableRoute.getId()));
+ assertThat(selectedRoutes).doesNotContain(selectableRoute.getId());
}
for (MediaRoute2Info deselectableRoute :
mManager.getDeselectableRoutes(newSessionInfo)) {
- assertTrue(selectedRoutes.contains(deselectableRoute.getId()));
+ assertThat(selectedRoutes).contains(deselectableRoute.getId());
}
onSessionCreatedLatch.countDown();
}
});
mManager.selectRoute(mPackageName, routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT));
- assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeFeatures)
@@ -984,7 +986,7 @@
mManager.registerCallback(mExecutor, callback);
try {
task.run();
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
} finally {
mManager.unregisterCallback(callback);
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
index 3fe0928..3955ff0 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
@@ -16,8 +16,7 @@
package com.android.mediaroutertest;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.content.res.Resources;
import android.media.MediaRoute2Info;
@@ -62,39 +61,39 @@
RoutingSessionInfo sessionInfoWithProviderId = new RoutingSessionInfo.Builder(sessionInfo)
.setProviderId(TEST_PROVIDER_ID).build();
- assertNotEquals(sessionInfo.getSelectedRoutes(),
- sessionInfoWithProviderId.getSelectedRoutes());
- assertNotEquals(sessionInfo.getSelectableRoutes(),
- sessionInfoWithProviderId.getSelectableRoutes());
- assertNotEquals(sessionInfo.getDeselectableRoutes(),
- sessionInfoWithProviderId.getDeselectableRoutes());
- assertNotEquals(sessionInfo.getTransferableRoutes(),
- sessionInfoWithProviderId.getTransferableRoutes());
+ assertThat(sessionInfoWithProviderId.getSelectedRoutes())
+ .isNotEqualTo(sessionInfo.getSelectedRoutes());
+ assertThat(sessionInfoWithProviderId.getSelectableRoutes())
+ .isNotEqualTo(sessionInfo.getSelectableRoutes());
+ assertThat(sessionInfoWithProviderId.getDeselectableRoutes())
+ .isNotEqualTo(sessionInfo.getDeselectableRoutes());
+ assertThat(sessionInfoWithProviderId.getTransferableRoutes())
+ .isNotEqualTo(sessionInfo.getTransferableRoutes());
RoutingSessionInfo sessionInfoWithOtherProviderId =
new RoutingSessionInfo.Builder(sessionInfoWithProviderId)
.setProviderId(TEST_OTHER_PROVIDER_ID).build();
- assertNotEquals(sessionInfoWithOtherProviderId.getSelectedRoutes(),
- sessionInfoWithProviderId.getSelectedRoutes());
- assertNotEquals(sessionInfoWithOtherProviderId.getSelectableRoutes(),
- sessionInfoWithProviderId.getSelectableRoutes());
- assertNotEquals(sessionInfoWithOtherProviderId.getDeselectableRoutes(),
- sessionInfoWithProviderId.getDeselectableRoutes());
- assertNotEquals(sessionInfoWithOtherProviderId.getTransferableRoutes(),
- sessionInfoWithProviderId.getTransferableRoutes());
+ assertThat(sessionInfoWithOtherProviderId.getSelectedRoutes())
+ .isNotEqualTo(sessionInfoWithProviderId.getSelectedRoutes());
+ assertThat(sessionInfoWithOtherProviderId.getSelectableRoutes())
+ .isNotEqualTo(sessionInfoWithProviderId.getSelectableRoutes());
+ assertThat(sessionInfoWithOtherProviderId.getDeselectableRoutes())
+ .isNotEqualTo(sessionInfoWithProviderId.getDeselectableRoutes());
+ assertThat(sessionInfoWithOtherProviderId.getTransferableRoutes())
+ .isNotEqualTo(sessionInfoWithProviderId.getTransferableRoutes());
RoutingSessionInfo sessionInfoWithProviderId2 =
new RoutingSessionInfo.Builder(sessionInfoWithProviderId).build();
- assertEquals(sessionInfoWithProviderId2.getSelectedRoutes(),
- sessionInfoWithProviderId.getSelectedRoutes());
- assertEquals(sessionInfoWithProviderId2.getSelectableRoutes(),
- sessionInfoWithProviderId.getSelectableRoutes());
- assertEquals(sessionInfoWithProviderId2.getDeselectableRoutes(),
- sessionInfoWithProviderId.getDeselectableRoutes());
- assertEquals(sessionInfoWithProviderId2.getTransferableRoutes(),
- sessionInfoWithProviderId.getTransferableRoutes());
+ assertThat(sessionInfoWithProviderId2.getSelectedRoutes())
+ .isEqualTo(sessionInfoWithProviderId.getSelectedRoutes());
+ assertThat(sessionInfoWithProviderId2.getSelectableRoutes())
+ .isEqualTo(sessionInfoWithProviderId.getSelectableRoutes());
+ assertThat(sessionInfoWithProviderId2.getDeselectableRoutes())
+ .isEqualTo(sessionInfoWithProviderId.getDeselectableRoutes());
+ assertThat(sessionInfoWithProviderId2.getTransferableRoutes())
+ .isEqualTo(sessionInfoWithProviderId.getTransferableRoutes());
}
@Test
@@ -114,6 +113,6 @@
? MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE :
MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
- assertEquals(sessionInfo.getVolumeHandling(), expectedResult);
+ assertThat(sessionInfo.getVolumeHandling()).isEqualTo(expectedResult);
}
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 4aa5bbf..1f96617 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -266,6 +266,7 @@
ASurfaceTransaction_setEnableBackPressure; # introduced=31
ASurfaceTransaction_setFrameRate; # introduced=30
ASurfaceTransaction_setFrameRateWithChangeStrategy; # introduced=31
+ ASurfaceTransaction_clearFrameRate; # introduced=34
ASurfaceTransaction_setFrameTimeline; # introduced=Tiramisu
ASurfaceTransaction_setGeometry; # introduced=29
ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index c2afc60..42f4406 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -619,6 +619,16 @@
transaction->setFrameRate(surfaceControl, frameRate, compatibility, changeFrameRateStrategy);
}
+void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ transaction->setFrameRate(surfaceControl, 0, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+}
+
void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl,
bool enableBackpressure) {
diff --git a/packages/BackupRestoreConfirmation/res/values-or/strings.xml b/packages/BackupRestoreConfirmation/res/values-or/strings.xml
index 1c54569..cb61092 100644
--- a/packages/BackupRestoreConfirmation/res/values-or/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-or/strings.xml
@@ -20,7 +20,7 @@
<string name="restore_confirm_title" msgid="5469365809567486602">"ସମ୍ପୂର୍ଣ୍ଣ ରିଷ୍ଟୋର୍"</string>
<string name="backup_confirm_text" msgid="1878021282758896593">"ଏକ ସଂଯୁକ୍ତ ଡେସ୍କଟପ୍ କମ୍ପ୍ୟୁଟର୍କୁ ସମସ୍ତ ଡାଟାର ସମ୍ପୂର୍ଣ୍ଣ ବ୍ୟାକଅପ୍ କରିବାକୁ ଅନୁରୋଧ କରାଯାଇଛି। ଆପଣ ଏହିପରି କରିବାକୁ ଚାହିଁବେ?\n\nଯଦି ଆପଣ ନିଜେ ବ୍ୟାକଅପ୍ର ଅନୁରୋଧ କରିନାହାନ୍ତି, ତେବେ ଏହି କାମକୁ ଆଗକୁ ବଢ଼ିବାକୁ ଦିଅନ୍ତୁ ନାହିଁ।"</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"ମୋ ଡାଟାର ବ୍ୟାକଅପ୍ ନିଆଯାଉ"</string>
- <string name="deny_backup_button_label" msgid="6009119115581097708">"ବ୍ୟାକଅପ୍ ନିଆନଯାଉ"</string>
+ <string name="deny_backup_button_label" msgid="6009119115581097708">"ବେକଅପ ନିଅନ୍ତୁ ନାହିଁ"</string>
<string name="restore_confirm_text" msgid="7499866728030461776">"ଏକ ସଂଯୁକ୍ତ ଡେସ୍କଟପ୍ କମ୍ପ୍ୟୁଟର୍ରୁ ସମସ୍ତ ଡାଟାର ସମ୍ପୂର୍ଣ୍ଣ ରିଷ୍ଟୋର୍ ଅନୁରୋଧ କରାଯାଇଛି। ଆପଣ ଏହାକୁ ଅନୁମତି ଦେବାକୁ ଚାହିଁବେ କି?\n\nଯଦି ଆପଣ ନିଜେ ରିଷ୍ଟୋର୍ ଅନୁରୋଧ କରିନାହାନ୍ତି, ତେବେ ଏହା କାର୍ଯ୍ୟକୁ ଆଗକୁ ବଢ଼ିବାକୁ ଦିଅନ୍ତୁ ନାହିଁ। ଏହା ବର୍ତ୍ତମାନ ଡିଭାଇସ୍ରେ ଥିବା ଯେକୌଣସି ଡାଟାକୁ ବଦଳାଇଦେବ!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"ମୋ ଡାଟାକୁ ରିଷ୍ଟୋର୍ କରାଯାଉ"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"ରିଷ୍ଟୋର୍ କରନ୍ତୁ ନାହିଁ।"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-pt-rPT/strings.xml b/packages/BackupRestoreConfirmation/res/values-pt-rPT/strings.xml
index a1e6167..1f6be83 100644
--- a/packages/BackupRestoreConfirmation/res/values-pt-rPT/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-pt-rPT/strings.xml
@@ -20,7 +20,7 @@
<string name="restore_confirm_title" msgid="5469365809567486602">"Restauro completo"</string>
<string name="backup_confirm_text" msgid="1878021282758896593">"Foi solicitada uma cópia de segurança completa de todos os dados para um computador. Permitir esta operação?\n\nCaso não tenha solicitado a cópia de segurança, não permita que a operação prossiga."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Fazer cópia de seg. dos dados"</string>
- <string name="deny_backup_button_label" msgid="6009119115581097708">"Não efetuar cópia de seg."</string>
+ <string name="deny_backup_button_label" msgid="6009119115581097708">"Não fazer cópia de seg."</string>
<string name="restore_confirm_text" msgid="7499866728030461776">"Foi solicitado um restauro completo de todos os dados a partir de um computador. Permitir esta operação?\n\nCaso não tenha solicitado o restauro, não permita que a operação prossiga. Isto substituirá os dados existentes no equipamento!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Restaurar os meus dados"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Não restaurar"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 867d70f..2d78b26 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -34,7 +34,7 @@
<string name="permission_notification_summary" msgid="884075314530071011">"इससे सभी सूचनाएं देखी जा सकती हैं. इसमें संपर्क, मैसेज, और फ़ोटो जैसी जानकारी शामिल होती है"</string>
<string name="permission_storage" msgid="6831099350839392343">"फ़ोटो और मीडिया"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
- <string name="helper_title_computer" msgid="4671071173916176037">"Google Play सेवाएं"</string>
+ <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> की ओर से, फ़ोन में मौजूद फ़ोटो, मीडिया, और सूचनाओं को ऐक्सेस करने की अनुमति मांग रहा है"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
diff --git a/packages/CompanionDeviceManager/res/values-night/themes.xml b/packages/CompanionDeviceManager/res/values-night/themes.xml
index 6eb16e7..55e91b6 100644
--- a/packages/CompanionDeviceManager/res/values-night/themes.xml
+++ b/packages/CompanionDeviceManager/res/values-night/themes.xml
@@ -18,8 +18,8 @@
<style name="ChooserActivity"
parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
- <item name="*android:windowFixedHeightMajor">100%</item>
- <item name="*android:windowFixedHeightMinor">100%</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index ff9f652..1698150 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -5,7 +5,7 @@
<string name="keyboard_layouts_label" msgid="6688773268302087545">"የAndroid የቁልፍ ሰሌዳ"</string>
<string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"እንግሊዝኛ (ዩኬ)"</string>
<string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"እንግሊዘኛ (ዩ.ኤስ.)"</string>
- <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"እንግሊዘኛ (ዩ. ኤስ.)፣ አለም አቀፍ ቅጥ"</string>
+ <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"እንግሊዘኛ (ዩ. ኤስ.)፣ ዓለም አቀፍ ቅጥ"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"እንግሊዘኛ (ዩ. ኤስ.)፣ የኮልማርክ ቅጥ"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"እንግሊዘኛ (ዩ. ኤስ.)፣ የድቮራክ ቅጥ"</string>
<string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"እንግሊዝኛ (አሜሪካ)፣ የሥራሰው ቅጥ"</string>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index 74f7d908..3fe7ba4 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -37,7 +37,7 @@
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Impossibile installare <xliff:g id="APP_NAME">%1$s</xliff:g> sulla TV."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"Impossibile installare <xliff:g id="APP_NAME">%1$s</xliff:g> sul telefono."</string>
<string name="launch" msgid="3952550563999890101">"Apri"</string>
- <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"L\'amministratore non consente l\'installazione di app ottenute da fonti sconosciute"</string>
+ <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"L\'amministratore non consente l\'installazione di app ottenute da origini sconosciute"</string>
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Questo utente non può installare app sconosciute"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non è autorizzato a installare app"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java
index b54cad6..40a9390 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java
@@ -18,12 +18,10 @@
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
public class DeviceUtils {
public static boolean isTelevision(Context context) {
- int uiMode = context.getResources().getConfiguration().uiMode;
- return (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
public static boolean isWear(final Context context) {
diff --git a/packages/SettingsLib/AppPreference/res/layout/app_header_preference.xml b/packages/SettingsLib/AppPreference/res/layout/app_header_preference.xml
new file mode 100644
index 0000000..a2389a9
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout/app_header_preference.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="24dp"
+ android:paddingBottom="16dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"/>
+
+ <TextView
+ android:id="@android:id/title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:gravity="center"
+ android:ellipsize="marquee"
+ android:textDirection="locale"
+ android:layout_marginTop="8dp"/>
+
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:text="@string/install_type_instant"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:singleLine="false"
+ android:textAlignment="center"/>
+
+ <TextView
+ android:id="@+id/second_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:singleLine="false"
+ android:maxLines="4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/AppPreference/res/values/strings.xml b/packages/SettingsLib/AppPreference/res/values/strings.xml
new file mode 100644
index 0000000..ca148c00
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Added as the value of a header field indicating this is an instant app (as opposed to installed normally) -->
+ <string name="install_type_instant">Instant app</string>
+</resources>
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java
new file mode 100644
index 0000000..60d00da
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java
@@ -0,0 +1,144 @@
+/*
+ * 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.settingslib.widget;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * The Preference for the pages need to show big apps icon and name in the header of the page.
+ */
+public class AppHeaderPreference extends Preference {
+
+ private boolean mIsInstantApp;
+ private CharSequence mSecondSummary;
+
+ public AppHeaderPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ public AppHeaderPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public AppHeaderPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public AppHeaderPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ setLayoutResource(R.layout.app_header_preference);
+ setSelectable(false);
+ setIsInstantApp(false);
+ }
+
+ /**
+ * Returns the installation type of this preference.
+ *
+ * @return whether the app is an instant app
+ * @see #setIsInstantApp(boolean)
+ */
+ public boolean getIsInstantApp() {
+ return mIsInstantApp;
+ }
+
+ /**
+ * Sets the installation type for this preference with a boolean.
+ *
+ * @param isInstantApp whether the app is an instant app
+ */
+ public void setIsInstantApp(@NonNull boolean isInstantApp) {
+ if (mIsInstantApp != isInstantApp) {
+ mIsInstantApp = isInstantApp;
+ notifyChanged();
+ }
+ }
+
+ /**
+ * Returns the second summary of this preference.
+ *
+ * @return The second summary
+ * @see #setSecondSummary(CharSequence)
+ */
+ @Nullable
+ public CharSequence getSecondSummary() {
+ return mSecondSummary;
+ }
+
+ /**
+ * Sets the second summary for this preference with a CharSequence.
+ *
+ * @param secondSummary The second summary for the preference
+ */
+ public void setSecondSummary(@Nullable CharSequence secondSummary) {
+ if (!TextUtils.equals(mSecondSummary, secondSummary)) {
+ mSecondSummary = secondSummary;
+ notifyChanged();
+ }
+ }
+
+ /**
+ * Sets the second summary for this preference with a resource ID.
+ *
+ * @param secondSummaryResId The second summary as a resource
+ * @see #setSecondSummary(CharSequence)
+ */
+ public void setSecondSummary(@StringRes int secondSummaryResId) {
+ setSecondSummary(getContext().getString(secondSummaryResId));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ final TextView installTypeView = (TextView) holder.findViewById(R.id.install_type);
+ if (installTypeView != null) {
+ if (mIsInstantApp) {
+ installTypeView.setVisibility(View.VISIBLE);
+ } else {
+ installTypeView.setVisibility(View.GONE);
+ }
+ }
+
+ final TextView secondSummaryView = (TextView) holder.findViewById(R.id.second_summary);
+ if (secondSummaryView != null) {
+ if (!TextUtils.isEmpty(mSecondSummary)) {
+ secondSummaryView.setText(mSecondSummary);
+ secondSummaryView.setVisibility(View.VISIBLE);
+ } else {
+ secondSummaryView.setVisibility(View.GONE);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/LayoutPreference/Android.bp b/packages/SettingsLib/LayoutPreference/Android.bp
index aaffdc9..c29e1f7 100644
--- a/packages/SettingsLib/LayoutPreference/Android.bp
+++ b/packages/SettingsLib/LayoutPreference/Android.bp
@@ -15,6 +15,7 @@
static_libs: [
"androidx.preference_preference",
+ "SettingsLibSettingsTheme",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
index 2bdd6fe..f958037 100644
--- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml
+++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
@@ -22,20 +22,6 @@
<item name="android:paddingEnd">16dp</item>
</style>
- <style name="TextAppearance.EntityHeaderTitle"
- parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:textSize">20sp</item>
- </style>
-
- <style name="TextAppearance.EntityHeaderSummary"
- parent="@android:style/TextAppearance.DeviceDefault">
- <item name="android:textAlignment">viewStart</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- <item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
- </style>
-
<style name="CrossProfileEntityHeaderIcon">
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
index 00bd141..328ab46 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -33,4 +33,18 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
+ <style name="TextAppearance.EntityHeaderTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">20sp</item>
+ </style>
+
+ <style name="TextAppearance.EntityHeaderSummary"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
+ </style>
+
</resources>
diff --git a/packages/SettingsLib/Spa/gallery/res/drawable/accessibility_captioning_banner.xml b/packages/SettingsLib/Spa/gallery/res/drawable/accessibility_captioning_banner.xml
new file mode 100644
index 0000000..6597ffb
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/res/drawable/accessibility_captioning_banner.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="412dp"
+ android:height="300dp"
+ android:viewportWidth="412"
+ android:viewportHeight="300">
+ <path
+ android:pathData="M383.9,300H28.1C12.6,300 0,287.4 0,271.9V28.1C0,12.6 12.6,0 28.1,0h355.8C399.4,0 412,12.6 412,28.1v243.8C412,287.4 399.4,300 383.9,300z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M79.2,179.6h53.6v8.5h-53.6z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M142.5,179.6h30.4v8.5h-30.4z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M79.2,195.5h79.2v8.5h-79.2z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M168.1,195.5h34.1v8.5h-34.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M211.9,195.5h34.1v8.5h-34.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M182.7,179.6h73.1v8.5h-73.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M265.5,179.6h26.8v8.5h-26.8z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M302.1,179.6h26.8v8.5h-26.8z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M142.7,67.9h-11.5c-1.6,0 -2.9,1.3 -2.9,2.9H67.8c-7.9,0 -14.4,6.5 -14.4,14.4v132.4c0,7.9 6.5,14.4 14.4,14.4h276.4c7.9,0 14.4,-6.5 14.4,-14.4V85.2c0,-7.9 -6.5,-14.4 -14.4,-14.4H203.1c0,-1.6 -1.3,-2.9 -2.9,-2.9h-28.8c-1.6,0 -2.9,1.3 -2.9,2.9h-23C145.5,69.2 144.3,67.9 142.7,67.9zM344.2,73.7c6.4,0 11.5,5.2 11.5,11.5v132.4c0,6.3 -5.2,11.5 -11.5,11.5H67.8c-6.4,0 -11.5,-5.2 -11.5,-11.5V85.2c0,-6.3 5.2,-11.5 11.5,-11.5H344.2z"
+ android:fillColor="#DADCE0"/>
+</vector>
diff --git a/packages/SettingsLib/Spa/gallery/res/raw/accessibility_shortcut_type_triple_tap.json b/packages/SettingsLib/Spa/gallery/res/raw/accessibility_shortcut_type_triple_tap.json
new file mode 100644
index 0000000..870e671
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/res/raw/accessibility_shortcut_type_triple_tap.json
@@ -0,0 +1,1959 @@
+{
+ "v": "5.6.5",
+ "fr": 60,
+ "ip": 0,
+ "op": 180,
+ "w": 412,
+ "h": 300,
+ "nm": "Triple_Tap_Screen",
+ "ddd": 0,
+ "assets": [
+ {
+ "id": "comp_0",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": ".white",
+ "cl": "white",
+ "hd": true,
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 15.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -15.4
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -15.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 15.4
+ ]
+ ],
+ "v": [
+ [
+ 178,
+ 150
+ ],
+ [
+ -178,
+ 150
+ ],
+ [
+ -206,
+ 122
+ ],
+ [
+ -206,
+ -122
+ ],
+ [
+ -178,
+ -150
+ ],
+ [
+ 178,
+ -150
+ ],
+ [
+ 206,
+ -122
+ ],
+ [
+ 206,
+ 122
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 1,
+ 1,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 1800,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": ".grey200",
+ "cl": "grey200",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 1.35,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -73.4
+ ],
+ [
+ -73.4,
+ 0
+ ],
+ [
+ 0,
+ 73.4
+ ],
+ [
+ 73.4,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ -73.4,
+ 0
+ ],
+ [
+ 0,
+ 73.4
+ ],
+ [
+ 73.4,
+ 0
+ ],
+ [
+ 0,
+ -73.4
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 1.4,
+ -132.9
+ ],
+ [
+ -131.6,
+ 0
+ ],
+ [
+ 1.3,
+ 132.9
+ ],
+ [
+ 134.3,
+ 0
+ ],
+ [
+ 1.4,
+ -132.9
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ind": 1,
+ "ty": "sh",
+ "ix": 2,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -24.7,
+ -24.8
+ ],
+ [
+ 0,
+ -35
+ ],
+ [
+ 24.8,
+ -24.7
+ ],
+ [
+ 35,
+ 0
+ ],
+ [
+ 24.7,
+ 24.8
+ ],
+ [
+ 0,
+ 35
+ ],
+ [
+ -24.8,
+ 24.7
+ ],
+ [
+ -35,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 35,
+ 0
+ ],
+ [
+ 24.7,
+ 24.7
+ ],
+ [
+ 0,
+ 35
+ ],
+ [
+ -24.7,
+ 24.7
+ ],
+ [
+ -35,
+ 0
+ ],
+ [
+ -24.7,
+ -24.8
+ ],
+ [
+ 0,
+ -35
+ ],
+ [
+ 24.7,
+ -24.7
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 1.4,
+ -130.9
+ ],
+ [
+ 94,
+ -92.5
+ ],
+ [
+ 132.4,
+ 0.1
+ ],
+ [
+ 94,
+ 92.7
+ ],
+ [
+ 1.4,
+ 131.1
+ ],
+ [
+ -91.2,
+ 92.7
+ ],
+ [
+ -129.6,
+ 0
+ ],
+ [
+ -91.2,
+ -92.6
+ ],
+ [
+ 1.4,
+ -130.9
+ ]
+ ],
+ "c": false
+ },
+ "ix": 2
+ },
+ "nm": "Path 2",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.909803926945,
+ 0.917647063732,
+ 0.929411768913,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 2,
+ "ty": 4,
+ "nm": ".grey300",
+ "cl": "grey300",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 205,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ -7.9,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 8
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1.6
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 1.5
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 6.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 6.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ -0.7
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 8,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1.6,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1.9,
+ -1.6
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 6.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -6.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ 0.7
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0.1,
+ 7.9
+ ]
+ ],
+ "v": [
+ [
+ -64,
+ 75.3
+ ],
+ [
+ 69.1,
+ 75.3
+ ],
+ [
+ 83.6,
+ 60.8
+ ],
+ [
+ 83.6,
+ -81
+ ],
+ [
+ 86.5,
+ -83.9
+ ],
+ [
+ 86.5,
+ -100.9
+ ],
+ [
+ 80.7,
+ -105.6
+ ],
+ [
+ 80.7,
+ 60.8
+ ],
+ [
+ 69.1,
+ 72.4
+ ],
+ [
+ -64,
+ 72.4
+ ],
+ [
+ -75.6,
+ 60.8
+ ],
+ [
+ -75.6,
+ -107.3
+ ],
+ [
+ -78.5,
+ -105.2
+ ],
+ [
+ -78.5,
+ 60.9
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.854901969433,
+ 0.86274510622,
+ 0.878431379795,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 1,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 3,
+ "ty": 4,
+ "nm": "cursor 5",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 36,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 39.582,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 44.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 55.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 37.791,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 59,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Ellipse 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 36,
+ "op": 59,
+ "st": -1,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 4,
+ "ty": 4,
+ "nm": "cursor 4",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 22,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 25.58,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 30.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 41.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 23.789,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 45,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Ellipse 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 22,
+ "op": 45,
+ "st": -3,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 5,
+ "ty": 4,
+ "nm": "cursor",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 8,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 11.582,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 16.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 27.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 9.791,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 31,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Ellipse 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 8,
+ "op": 31,
+ "st": -5,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 6,
+ "ty": 0,
+ "nm": "BG_White",
+ "refId": "comp_0",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "w": 412,
+ "h": 300,
+ "ip": 0,
+ "op": 1800,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
index 6e6da52..a063847 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
@@ -18,4 +18,4 @@
import com.android.settingslib.spa.framework.BrowseActivity
-class MainActivity : BrowseActivity(galleryPageProviders)
+class MainActivity : BrowseActivity(SpaEnvironment.PageProviderRepository)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
index 0d94734..e75e76c 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
@@ -16,32 +16,49 @@
package com.android.settingslib.spa.gallery
+import com.android.settingslib.spa.framework.common.SettingsEntryRepository
+import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
+import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
+import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
-val galleryPageProviders = SettingsPageProviderRepository(
- allPagesList = listOf(
- HomePageProvider,
- PreferenceMainPageProvider,
- PreferencePageProvider,
- SwitchPreferencePageProvider,
- TwoTargetSwitchPreferencePageProvider,
- ArgumentPageProvider,
- SliderPageProvider,
- SpinnerPageProvider,
- SettingsPagerPageProvider,
- FooterPageProvider,
- ),
- rootPages = listOf(HomePageProvider.name)
-)
+object SpaEnvironment {
+ val PageProviderRepository: SettingsPageProviderRepository by
+ lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
+ SettingsPageProviderRepository(
+ allPageProviders = listOf(
+ HomePageProvider,
+ PreferenceMainPageProvider,
+ PreferencePageProvider,
+ SwitchPreferencePageProvider,
+ MainSwitchPreferencePageProvider,
+ TwoTargetSwitchPreferencePageProvider,
+ ArgumentPageProvider,
+ SliderPageProvider,
+ SpinnerPageProvider,
+ SettingsPagerPageProvider,
+ FooterPageProvider,
+ IllustrationPageProvider,
+ ),
+ rootPages = listOf(
+ SettingsPage(HomePageProvider.name)
+ ) + ArgumentPageProvider.buildRootPages()
+ )
+ }
-// TODO: add other environment setup here.
+ val EntryRepository: SettingsEntryRepository by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
+ SettingsEntryRepository(PageProviderRepository)
+ }
+
+ // TODO: add other environment setup here.
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
index fdcb1c2..2428bba 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
@@ -17,14 +17,18 @@
package com.android.settingslib.spa.gallery.home
import android.os.Bundle
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
+import com.android.settingslib.spa.gallery.SpaEnvironment
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
+import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
@@ -50,6 +54,18 @@
SpinnerPageProvider.EntryItem()
SettingsPagerPageProvider.EntryItem()
FooterPageProvider.EntryItem()
+ IllustrationPageProvider.EntryItem()
+
+ /**
+ * A test button to generate hierarchy.
+ * TODO: remove it once the content provider is ready.
+ */
+ Button(onClick = {
+ SpaEnvironment.EntryRepository.printAllPages()
+ SpaEnvironment.EntryRepository.printAllEntries()
+ }) {
+ Text(text = "Generate Entry")
+ }
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
index 92b64fb4..5fe17e5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
@@ -19,58 +19,46 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
-import androidx.navigation.NavType
-import androidx.navigation.navArgument
-import com.android.settingslib.spa.framework.common.PageArguments
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
-import com.android.settingslib.spa.framework.common.SettingsPageBuilder
+import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
-private const val TITLE = "Sample page with arguments"
-private const val STRING_PARAM_NAME = "stringParam"
-private const val INT_PARAM_NAME = "intParam"
-
object ArgumentPageProvider : SettingsPageProvider {
- override val name = "Argument"
+ override val name = ArgumentPageModel.name
- override val arguments = listOf(
- navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
- navArgument(INT_PARAM_NAME) { type = NavType.IntType },
- )
+ override val parameter = ArgumentPageModel.parameter
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
- val pageArgs = PageArguments(ArgumentPageProvider.arguments, arguments)
- if (!pageArgs.isValid()) return emptyList()
+ if (!ArgumentPageModel.isValidArgument(arguments)) return emptyList()
- val owner = SettingsPageBuilder.create(name, pageArgs.normalize()).build()
+ val owner = SettingsPage.create(name, parameter, arguments)
val entryList = mutableListOf<SettingsEntry>()
- val stringParamEntry = SettingsEntryBuilder.create("string_param", owner)
- stringParamEntry.setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "String param value"
- override val summary = pageArgs.getStringArg(STRING_PARAM_NAME)!!.toState()
- })
- }
- entryList.add(stringParamEntry.build())
+ entryList.add(
+ SettingsEntryBuilder.create("string_param", owner)
+ // Set attributes
+ .setIsAllowSearch(true)
+ .setUiLayoutFn {
+ // Set ui rendering
+ Preference(ArgumentPageModel.create(arguments).genStringParamPreferenceModel())
+ }.build()
+ )
- val intParamEntry = SettingsEntryBuilder.create("int_param", owner)
- intParamEntry.setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Int param value"
- override val summary = pageArgs.getIntArg(INT_PARAM_NAME)!!.toString().toState()
- })
- }
- entryList.add(intParamEntry.build())
+ entryList.add(
+ SettingsEntryBuilder.create("int_param", owner)
+ // Set attributes
+ .setIsAllowSearch(true)
+ .setUiLayoutFn {
+ // Set ui rendering
+ Preference(ArgumentPageModel.create(arguments).genIntParamPreferenceModel())
+ }.build()
+ )
- val entryFoo = buildInjectEntry(pageArgs.buildNextArg("foo"))
- val entryBar = buildInjectEntry(pageArgs.buildNextArg("bar"))
+ val entryFoo = buildInjectEntry(ArgumentPageModel.buildNextArgument("foo", arguments))
+ val entryBar = buildInjectEntry(ArgumentPageModel.buildNextArgument("bar", arguments))
if (entryFoo != null) entryList.add(entryFoo.setLink(fromPage = owner).build())
if (entryBar != null) entryList.add(entryBar.setLink(fromPage = owner).build())
@@ -78,30 +66,27 @@
}
private fun buildInjectEntry(arguments: Bundle?): SettingsEntryBuilder? {
- val pageArgs = PageArguments(ArgumentPageProvider.arguments, arguments)
- if (!pageArgs.isValid()) return null
+ if (!ArgumentPageModel.isValidArgument(arguments)) return null
- val seBuilder = SettingsEntryBuilder.createLinkTo("injection", name, pageArgs.normalize())
- seBuilder.setIsAllowSearch(false)
+ return SettingsEntryBuilder.createInject(SettingsPage.create(name, parameter, arguments))
+ // Set attributes
+ .setIsAllowSearch(false)
+ .setUiLayoutFn {
+ // Set ui rendering
+ Preference(ArgumentPageModel.create(arguments).genInjectPreferenceModel())
+ }
+ }
- seBuilder.setUiLayoutFn {
- val summaryArray = listOf(
- "$STRING_PARAM_NAME=" + pageArgs.getStringArg(STRING_PARAM_NAME)!!,
- "$INT_PARAM_NAME=" + pageArgs.getIntArg(INT_PARAM_NAME)!!
- )
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val summary = summaryArray.joinToString(", ").toState()
- override val onClick = navigator(name + pageArgs.navLink())
- })
- }
-
- return seBuilder
+ fun buildRootPages(): List<SettingsPage> {
+ return listOf(
+ SettingsPage.create(name, parameter, ArgumentPageModel.buildArgument("foo")),
+ SettingsPage.create(name, parameter, ArgumentPageModel.buildArgument("bar")),
+ )
}
@Composable
override fun Page(arguments: Bundle?) {
- RegularScaffold(title = TITLE) {
+ RegularScaffold(title = ArgumentPageModel.create(arguments).genPageTitle()) {
for (entry in buildEntry(arguments)) {
entry.uiLayout()
}
@@ -110,7 +95,8 @@
@Composable
fun EntryItem(stringParam: String, intParam: Int) {
- buildInjectEntry(buildArgument(stringParam, intParam))?.build()?.uiLayout?.let { it() }
+ buildInjectEntry(ArgumentPageModel.buildArgument(stringParam, intParam))
+ ?.build()?.uiLayout?.let { it() }
}
}
@@ -118,26 +104,8 @@
@Composable
private fun ArgumentPagePreview() {
SettingsTheme {
- ArgumentPageProvider.Page(buildArgument(stringParam = "foo", intParam = 0))
+ ArgumentPageProvider.Page(
+ ArgumentPageModel.buildArgument(stringParam = "foo", intParam = 0)
+ )
}
}
-
-private fun PageArguments.isValid(): Boolean {
- val stringParam = getStringArg(STRING_PARAM_NAME)
- return (stringParam != null && listOf("foo", "bar").contains(stringParam))
-}
-
-private fun PageArguments.buildNextArg(stringParam: String): Bundle {
- val intParam = getIntArg(INT_PARAM_NAME)
- return if (intParam == null)
- buildArgument(stringParam)
- else
- buildArgument(stringParam, intParam + 1)
-}
-
-private fun buildArgument(stringParam: String, intParam: Int? = null): Bundle {
- val args = Bundle()
- args.putString(STRING_PARAM_NAME, stringParam)
- if (intParam != null) args.putInt(INT_PARAM_NAME, intParam)
- return args
-}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
new file mode 100644
index 0000000..8b94342
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.settingslib.spa.gallery.page
+
+import android.os.Bundle
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import com.android.settingslib.spa.framework.common.PageModel
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.util.getIntArg
+import com.android.settingslib.spa.framework.util.getStringArg
+import com.android.settingslib.spa.framework.util.navLink
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+private const val TITLE = "Sample page with arguments"
+private const val STRING_PARAM_NAME = "stringParam"
+private const val INT_PARAM_NAME = "intParam"
+
+class ArgumentPageModel : PageModel() {
+
+ companion object {
+ const val name = "Argument"
+ val parameter = listOf(
+ navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
+ navArgument(INT_PARAM_NAME) { type = NavType.IntType },
+ )
+
+ fun buildArgument(stringParam: String, intParam: Int? = null): Bundle {
+ val args = Bundle()
+ args.putString(STRING_PARAM_NAME, stringParam)
+ if (intParam != null) args.putInt(INT_PARAM_NAME, intParam)
+ return args
+ }
+
+ fun buildNextArgument(newStringParam: String, arguments: Bundle? = null): Bundle {
+ val intParam = parameter.getIntArg(INT_PARAM_NAME, arguments)
+ return if (intParam == null)
+ buildArgument(newStringParam)
+ else
+ buildArgument(newStringParam, intParam + 1)
+ }
+
+ fun isValidArgument(arguments: Bundle?): Boolean {
+ val stringParam = parameter.getStringArg(STRING_PARAM_NAME, arguments)
+ return (stringParam != null && listOf("foo", "bar").contains(stringParam))
+ }
+
+ @Composable
+ fun create(arguments: Bundle?): ArgumentPageModel {
+ val pageModel: ArgumentPageModel = viewModel(key = arguments.toString())
+ pageModel.initOnce(arguments)
+ return pageModel
+ }
+ }
+
+ private val title = TITLE
+ private var arguments: Bundle? = null
+ private var stringParam: String? = null
+ private var intParam: Int? = null
+
+ override fun initialize(arguments: Bundle?) {
+ logMsg("init with args " + arguments.toString())
+ this.arguments = arguments
+ stringParam = parameter.getStringArg(STRING_PARAM_NAME, arguments)
+ intParam = parameter.getIntArg(INT_PARAM_NAME, arguments)
+ }
+
+ @Composable
+ fun genPageTitle(): String {
+ return title
+ }
+
+ @Composable
+ fun genStringParamPreferenceModel(): PreferenceModel {
+ return object : PreferenceModel {
+ override val title = "String param value"
+ override val summary = stateOf(stringParam!!)
+ }
+ }
+
+ @Composable
+ fun genIntParamPreferenceModel(): PreferenceModel {
+ return object : PreferenceModel {
+ override val title = "Int param value"
+ override val summary = stateOf(intParam!!.toString())
+ }
+ }
+
+ @Composable
+ fun genInjectPreferenceModel(): PreferenceModel {
+ val summaryArray = listOf(
+ "$STRING_PARAM_NAME=" + stringParam!!,
+ "$INT_PARAM_NAME=" + intParam!!
+ )
+ return object : PreferenceModel {
+ override val title = genPageTitle()
+ override val summary = stateOf(summaryArray.joinToString(", "))
+ override val onClick = navigator(name + parameter.navLink(arguments))
+ }
+ }
+}
+
+private fun logMsg(message: String) {
+ Log.d("ArgumentPageModel", message)
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt
new file mode 100644
index 0000000..4f8ea0b
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.settingslib.spa.gallery.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.gallery.R
+import com.android.settingslib.spa.widget.Illustration
+import com.android.settingslib.spa.widget.IllustrationModel
+import com.android.settingslib.spa.widget.ResourceType
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+object IllustrationPageProvider : SettingsPageProvider {
+ override val name = "Illustration"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ IllustrationPage()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = "Sample Illustration"
+ override val onClick = navigator(name)
+ })
+ }
+}
+
+@Composable
+private fun IllustrationPage() {
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ Preference(object : PreferenceModel {
+ override val title = "Lottie Illustration"
+ })
+
+ Illustration(object : IllustrationModel {
+ override val resId = R.raw.accessibility_shortcut_type_triple_tap
+ override val resourceType = ResourceType.LOTTIE
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Image Illustration"
+ })
+
+ Illustration(object : IllustrationModel {
+ override val resId = R.drawable.accessibility_captioning_banner
+ override val resourceType = ResourceType.IMAGE
+ })
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun IllustrationPagePreview() {
+ SettingsTheme {
+ IllustrationPage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt
new file mode 100644
index 0000000..53d7648
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.settingslib.spa.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.MainSwitchPreference
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample MainSwitchPreference"
+
+object MainSwitchPreferencePageProvider : SettingsPageProvider {
+ override val name = "MainSwitchPreference"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ MainSwitchPreferencePage()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+}
+
+@Composable
+private fun MainSwitchPreferencePage() {
+ RegularScaffold(title = TITLE) {
+ SampleMainSwitchPreference()
+ SampleNotChangeableMainSwitchPreference()
+ }
+}
+
+@Composable
+private fun SampleMainSwitchPreference() {
+ val checked = rememberSaveable { mutableStateOf(false) }
+ MainSwitchPreference(remember {
+ object : SwitchPreferenceModel {
+ override val title = "MainSwitchPreference"
+ override val checked = checked
+ override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ }
+ })
+}
+
+@Composable
+private fun SampleNotChangeableMainSwitchPreference() {
+ val checked = rememberSaveable { mutableStateOf(true) }
+ MainSwitchPreference(remember {
+ object : SwitchPreferenceModel {
+ override val title = "Not changeable"
+ override val changeable = stateOf(false)
+ override val checked = checked
+ override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ }
+ })
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun MainSwitchPreferencePagePreview() {
+ SettingsTheme {
+ MainSwitchPreferencePage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
index e71c30c..0a8dae3 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
@@ -48,6 +48,7 @@
RegularScaffold(title = TITLE) {
PreferencePageProvider.EntryItem()
SwitchPreferencePageProvider.EntryItem()
+ MainSwitchPreferencePageProvider.EntryItem()
TwoTargetSwitchPreferencePageProvider.EntryItem()
}
}
diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp
index 3c8d91e..a4928e6 100644
--- a/packages/SettingsLib/Spa/spa/Android.bp
+++ b/packages/SettingsLib/Spa/spa/Android.bp
@@ -30,6 +30,7 @@
"androidx.compose.ui_ui-tooling-preview",
"androidx.navigation_navigation-compose",
"com.google.android.material_material",
+ "lottie_compose",
],
kotlincflags: [
"-Xjvm-default=all",
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index ad69da31..104966d 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -66,4 +66,5 @@
api 'androidx.navigation:navigation-compose:2.5.0'
api 'com.google.android.material:material:1.6.1'
debugApi "androidx.compose.ui:ui-tooling:$jetpack_compose_version"
+ implementation 'com.airbnb.android:lottie-compose:5.2.0'
}
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 e8d1ea2..87d4f5a 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
@@ -27,10 +27,10 @@
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.android.settingslib.spa.R
-import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.util.navRoute
open class BrowseActivity(
private val sppRepository: SettingsPageProviderRepository,
@@ -55,8 +55,8 @@
NavHost(navController, sppRepository.getDefaultStartPageName()) {
for (page in sppRepository.getAllProviders()) {
composable(
- route = page.route,
- arguments = page.arguments,
+ route = page.name + page.parameter.navRoute(),
+ arguments = page.parameter,
) { navBackStackEntry ->
page.Page(navBackStackEntry.arguments)
}
@@ -75,9 +75,6 @@
}
}
- private val SettingsPageProvider.route: String
- get() = name + arguments.joinToString("") { argument -> "/{${argument.name}}" }
-
companion object {
const val KEY_DESTINATION = "spa:SpaActivity:destination"
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageArguments.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageArguments.kt
deleted file mode 100644
index c0f585f..0000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageArguments.kt
+++ /dev/null
@@ -1,74 +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.settingslib.spa.framework.common
-
-import android.os.Bundle
-import androidx.navigation.NamedNavArgument
-import androidx.navigation.NavType
-
-class PageArguments(
- private val navArgsDef: List<NamedNavArgument>,
- private val rawArgs: Bundle?
-) {
- fun normalize(): Bundle {
- if (rawArgs == null) return Bundle.EMPTY
- val normArgs = Bundle()
- for (navArg in navArgsDef) {
- when (navArg.argument.type) {
- NavType.StringType -> {
- val value = rawArgs.getString(navArg.name)
- if (value != null) normArgs.putString(navArg.name, value)
- }
- NavType.IntType -> {
- if (rawArgs.containsKey(navArg.name))
- normArgs.putInt(navArg.name, rawArgs.getInt(navArg.name))
- }
- }
- }
- return normArgs
- }
-
- fun navLink(): String{
- if (rawArgs == null) return ""
- val argsArray = mutableListOf<String>()
- for (navArg in navArgsDef) {
- when (navArg.argument.type) {
- NavType.StringType -> {
- argsArray.add(rawArgs.getString(navArg.name, ""))
- }
- NavType.IntType -> {
- argsArray.add(rawArgs.getInt(navArg.name).toString())
- }
- }
- }
- return argsArray.joinToString("") {arg -> "/$arg" }
- }
-
- fun getStringArg(key: String): String? {
- if (rawArgs != null && rawArgs.containsKey(key)) {
- return rawArgs.getString(key)
- }
- return null
- }
-
- fun getIntArg(key: String): Int? {
- if (rawArgs != null && rawArgs.containsKey(key)) {
- return rawArgs.getInt(key)
- }
- return null
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
similarity index 60%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
copy to packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
index e62a63a..ee12f02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode;
+package com.android.settingslib.spa.framework.common
-import android.os.SystemProperties;
+import android.os.Bundle
+import androidx.lifecycle.ViewModel
-/**
- * Constants for desktop mode feature
- */
-public class DesktopModeConstants {
+open class PageModel : ViewModel() {
+ var initialized = false
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+ fun initOnce(arguments: Bundle?) {
+ // Initialize only once
+ if (initialized) return
+ initialized = true
+ initialize(arguments)
+ }
+
+ open fun initialize(arguments: Bundle?) {}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
index 6fcd555..98734da 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
@@ -18,6 +18,11 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
+import androidx.navigation.NamedNavArgument
+import com.android.settingslib.spa.framework.util.normalize
+
+const val INJECT_ENTRY_NAME = "INJECT"
+const val ROOT_ENTRY_NAME = "ROOT"
/**
* Defines data of one Settings entry for Settings search.
@@ -32,18 +37,38 @@
/**
* Defines data to identify a Settings page.
*/
-data class SettingsPage(val name: String = "", val args: Bundle = Bundle.EMPTY)
+data class SettingsPage(val name: String = "", val arguments: Bundle? = null) {
+ override fun toString(): String {
+ val argsStr = arguments?.toString()?.removeRange(0, 6) ?: ""
+ return name + argsStr
+ }
+
+ companion object {
+ fun create(
+ name: String,
+ parameter: List<NamedNavArgument> = emptyList(),
+ arguments: Bundle? = null
+ ): SettingsPage {
+ return SettingsPage(name, parameter.normalize(arguments))
+ }
+ }
+}
/**
* Defines data of a Settings entry.
*/
data class SettingsEntry(
+ // The unique id of this entry.
+ // By default, it is computed by name + owner + fromPage + toPage
+ val id: String,
+
+ // The display name of this entry, which is used to be shown in hierarchy.
+ // By default, it is computed by name + owner
+ val displayName: String,
+
val name: String,
val owner: SettingsPage,
- // Generates the unique id of this entry
- val uniqueId: String,
-
// Defines linking of Settings entries
val fromPage: SettingsPage? = null,
val toPage: SettingsPage? = null,
@@ -80,39 +105,9 @@
* use each entries' UI rendering function in the page instead.
*/
val uiLayout: (@Composable () -> Unit) = {},
-)
-
-/**
- * The helper to build a Settings Page instance.
- */
-class SettingsPageBuilder(private val name: String) {
- private val arguments = Bundle()
-
- fun pushArgs(args: Bundle?): SettingsPageBuilder {
- if (args != null) arguments.putAll(args)
- return this
- }
-
- fun pushArg(argName: String, argValue: String?): SettingsPageBuilder {
- if (argValue != null) this.arguments.putString(argName, argValue)
- return this
- }
-
- fun pushArg(argName: String, argValue: Int?): SettingsPageBuilder {
- if (argValue != null) this.arguments.putInt(argName, argValue)
- return this
- }
-
- fun build(): SettingsPage {
- return SettingsPage(name, arguments)
- }
-
- companion object {
- fun create(name: String, args: Bundle? = null): SettingsPageBuilder {
- return SettingsPageBuilder(name).apply {
- pushArgs(args)
- }
- }
+) {
+ override fun toString(): String {
+ return displayName + "(${fromPage?.toString()}->${toPage?.toString()})"
}
}
@@ -121,6 +116,7 @@
*/
class SettingsEntryBuilder(private val name: String, private val owner: SettingsPage) {
private var uniqueId: String? = null
+ private var displayName: String? = null
private var fromPage: SettingsPage? = null
private var toPage: SettingsPage? = null
private var isAllowSearch: Boolean? = null
@@ -130,9 +126,10 @@
fun build(): SettingsEntry {
return SettingsEntry(
+ id = computeUniqueId(),
+ displayName = computeDisplayName(),
name = name,
owner = owner,
- uniqueId = computeUniqueId(),
// linking data
fromPage = fromPage,
@@ -171,35 +168,32 @@
return this
}
- private fun computeUniqueId(): String = uniqueId ?: (name + owner.toString())
+ private fun computeUniqueId(): String =
+ uniqueId ?: "$owner:$name" + fromPage?.toString() + toPage?.toString()
+
+ private fun computeDisplayName(): String = displayName ?: "$owner:$name"
private fun computeSearchable(): Boolean = isAllowSearch ?: false
companion object {
- fun create(
- entryName: String,
- ownerPageName: String,
- ownerPageArgs: Bundle? = null
- ): SettingsEntryBuilder {
- val owner = SettingsPageBuilder.create(ownerPageName, ownerPageArgs)
- return SettingsEntryBuilder(entryName, owner.build())
- }
-
fun create(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
return SettingsEntryBuilder(entryName, owner)
}
- fun createLinkTo(
- entryName: String,
- ownerPageName: String,
- ownerPageArgs: Bundle? = null
- ): SettingsEntryBuilder {
- val owner = SettingsPageBuilder.create(ownerPageName, ownerPageArgs)
- return SettingsEntryBuilder(entryName, owner.build()).setLink(toPage = owner.build())
+ fun createLinkFrom(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
+ return create(entryName, owner).setLink(fromPage = owner)
}
fun createLinkTo(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
- return SettingsEntryBuilder(entryName, owner).setLink(toPage = owner)
+ return create(entryName, owner).setLink(toPage = owner)
+ }
+
+ fun createInject(owner: SettingsPage): SettingsEntryBuilder {
+ return createLinkTo(INJECT_ENTRY_NAME, owner)
+ }
+
+ fun createRoot(page: SettingsPage): SettingsEntryBuilder {
+ return createLinkTo(ROOT_ENTRY_NAME, page)
}
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
new file mode 100644
index 0000000..e3a55e5
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.settingslib.spa.framework.common
+
+import android.util.Log
+import java.util.LinkedList
+
+private const val MAX_ENTRY_SIZE = 5000
+
+/**
+ * The repository to maintain all Settings entries
+ */
+class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
+ // Map of entry unique Id to entry
+ private val entryMap: Map<String, SettingsEntry>
+
+ // Map of Settings page to its contained entries.
+ private val pageToEntryListMap: Map<String, List<SettingsEntry>>
+
+ init {
+ logMsg("Initialize")
+ entryMap = mutableMapOf()
+ pageToEntryListMap = mutableMapOf()
+
+ val entryQueue = LinkedList<SettingsEntry>()
+ for (page in sppRepository.getAllRootPages()) {
+ val rootEntry = SettingsEntryBuilder.createRoot(page).build()
+ if (!entryMap.containsKey(rootEntry.id)) {
+ entryQueue.push(rootEntry)
+ entryMap.put(rootEntry.id, rootEntry)
+ }
+ }
+
+ while (entryQueue.isNotEmpty() && entryMap.size < MAX_ENTRY_SIZE) {
+ val entry = entryQueue.pop()
+ val page = entry.toPage
+ if (page == null || pageToEntryListMap.containsKey(page.toString())) continue
+ val spp = sppRepository.getProviderOrNull(page.name) ?: continue
+ val newEntries = spp.buildEntry(page.arguments)
+ pageToEntryListMap[page.toString()] = newEntries
+ for (newEntry in newEntries) {
+ if (!entryMap.containsKey(newEntry.id)) {
+ entryQueue.push(newEntry)
+ entryMap.put(newEntry.id, newEntry)
+ }
+ }
+ }
+
+ logMsg("Initialize Completed: ${entryMap.size} entries in ${pageToEntryListMap.size} pages")
+ }
+
+ fun printAllPages() {
+ for (entry in pageToEntryListMap.entries) {
+ logMsg("page: ${entry.key} with ${entry.value.size} entries")
+ }
+ }
+
+ fun printAllEntries() {
+ for (entry in entryMap.values) {
+ logMsg("entry: $entry")
+ }
+ }
+}
+
+private fun logMsg(message: String) {
+ Log.d("EntryRepo", message)
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
index a57c66e..965c2b3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
@@ -28,8 +28,8 @@
/** The page name without arguments. */
val name: String
- /** The page arguments, default is no arguments. */
- val arguments: List<NamedNavArgument>
+ /** The page parameters, default is no parameters. */
+ val parameter: List<NamedNavArgument>
get() = emptyList()
/** The [Composable] used to render this page. */
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
index 0a32939..6adda6b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
@@ -16,24 +16,41 @@
package com.android.settingslib.spa.framework.common
+import android.util.Log
+
class SettingsPageProviderRepository(
- allPagesList: List<SettingsPageProvider>,
- private val rootPages: List<String>
+ allPageProviders: List<SettingsPageProvider>,
+ private val rootPages: List<SettingsPage> = emptyList(),
) {
- // Maintains all SPA page providers.
- private val allPages: Map<String, SettingsPageProvider> = allPagesList.associateBy { it.name }
+ // Map of page name to its provider.
+ private val pageProviderMap: Map<String, SettingsPageProvider>
+
+ init {
+ pageProviderMap = allPageProviders.associateBy { it.name }
+ logMsg("Initialize Completed: ${pageProviderMap.size} spp")
+ }
fun getDefaultStartPageName(): String {
- return rootPages.getOrElse(0) {
- return ""
+ return if (rootPages.isNotEmpty()) {
+ rootPages[0].name
+ } else {
+ ""
}
}
+ fun getAllRootPages(): Collection<SettingsPage> {
+ return rootPages
+ }
+
fun getAllProviders(): Collection<SettingsPageProvider> {
- return allPages.values
+ return pageProviderMap.values
}
fun getProviderOrNull(name: String): SettingsPageProvider? {
- return allPages[name]
+ return pageProviderMap[name]
}
}
+
+private fun logMsg(message: String) {
+ Log.d("SppRepo", message)
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index c1a3c20..c2223e6 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -39,4 +39,10 @@
/** The size when app icon is displayed in App info page. */
val appIconInfoSize = 48.dp
+
+ /** The sizes info of illustration widget. */
+ val illustrationMaxWidth = 412.dp
+ val illustrationMaxHeight = 300.dp
+ val illustrationPadding = 16.dp
+ val illustrationCornerRadius = 28.dp
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
index 20020d0..c66e20a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
@@ -21,4 +21,6 @@
object SettingsShape {
val CornerMedium = RoundedCornerShape(12.dp)
+
+ val CornerLarge = RoundedCornerShape(24.dp)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Collections.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Collections.kt
index ba25336..9478587 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Collections.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Collections.kt
@@ -19,7 +19,23 @@
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+/**
+ * Performs the given [action] on each element asynchronously.
+ */
+suspend inline fun <T> Iterable<T>.asyncForEach(crossinline action: (T) -> Unit) {
+ coroutineScope {
+ forEach {
+ launch { action(it) }
+ }
+ }
+}
+
+/**
+ * Returns a list containing the results of asynchronously applying the given [transform] function
+ * to each element in the original collection.
+ */
suspend inline fun <R, T> Iterable<T>.asyncMap(crossinline transform: (T) -> R): List<R> =
coroutineScope {
map { item ->
@@ -27,6 +43,11 @@
}.awaitAll()
}
+/**
+ * Returns a list containing only elements matching the given [predicate].
+ *
+ * The filter operation is done asynchronously.
+ */
suspend inline fun <T> Iterable<T>.asyncFilter(crossinline predicate: (T) -> Boolean): List<T> =
asyncMap { item -> item to predicate(item) }
.filter { it.second }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Parameter.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Parameter.kt
new file mode 100644
index 0000000..4e768eb
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Parameter.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.settingslib.spa.framework.util
+
+import android.os.Bundle
+import androidx.navigation.NamedNavArgument
+import androidx.navigation.NavType
+
+fun List<NamedNavArgument>.navRoute(): String {
+ return this.joinToString("") { argument -> "/{${argument.name}}" }
+}
+
+fun List<NamedNavArgument>.navLink(arguments: Bundle? = null): String {
+ if (arguments == null) return ""
+ val argsArray = mutableListOf<String>()
+ for (navArg in this) {
+ when (navArg.argument.type) {
+ NavType.StringType -> {
+ argsArray.add(arguments.getString(navArg.name, ""))
+ }
+ NavType.IntType -> {
+ argsArray.add(arguments.getInt(navArg.name).toString())
+ }
+ }
+ }
+ return argsArray.joinToString("") { arg -> "/$arg" }
+}
+
+fun List<NamedNavArgument>.normalize(arguments: Bundle? = null): Bundle? {
+ if (this.isEmpty()) return null
+ val normArgs = Bundle()
+ for (navArg in this) {
+ when (navArg.argument.type) {
+ NavType.StringType -> {
+ val value = arguments?.getString(navArg.name)
+ if (value != null)
+ normArgs.putString(navArg.name, value)
+ else
+ normArgs.putString("unset_" + navArg.name, null)
+ }
+ NavType.IntType -> {
+ if (arguments != null && arguments.containsKey(navArg.name))
+ normArgs.putInt(navArg.name, arguments.getInt(navArg.name))
+ else
+ normArgs.putString("unset_" + navArg.name, null)
+ }
+ }
+ }
+ return normArgs
+}
+
+fun List<NamedNavArgument>.getStringArg(name: String, arguments: Bundle? = null): String? {
+ if (this.containsStringArg(name) && arguments != null) {
+ return arguments.getString(name)
+ }
+ return null
+}
+
+fun List<NamedNavArgument>.getIntArg(name: String, arguments: Bundle? = null): Int? {
+ if (this.containsIntArg(name) && arguments != null && arguments.containsKey(name)) {
+ return arguments.getInt(name)
+ }
+ return null
+}
+
+fun List<NamedNavArgument>.containsStringArg(name: String): Boolean {
+ for (navArg in this) {
+ if (navArg.argument.type == NavType.StringType && navArg.name == name) return true
+ }
+ return false
+}
+
+fun List<NamedNavArgument>.containsIntArg(name: String): Boolean {
+ for (navArg in this) {
+ if (navArg.argument.type == NavType.IntType && navArg.name == name) return true
+ }
+ return false
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/Illustration.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/Illustration.kt
new file mode 100644
index 0000000..cd8a02a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/Illustration.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.settingslib.spa.widget
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.ui.ImageBox
+import com.android.settingslib.spa.widget.ui.Lottie
+enum class ResourceType { IMAGE, LOTTIE }
+
+/**
+ * The widget model for [Illustration] widget.
+ */
+interface IllustrationModel {
+ /**
+ * The resource id of this [Illustration].
+ */
+ val resId: Int
+
+ /**
+ * The resource type of the [Illustration].
+ *
+ * It should be Lottie or Image.
+ */
+ val resourceType: ResourceType
+}
+
+/**
+ * Illustration widget.
+ *
+ * Data is provided through [IllustrationModel].
+ */
+@Composable
+fun Illustration(model: IllustrationModel) {
+ Illustration(
+ resId = model.resId,
+ resourceType = model.resourceType,
+ modifier = Modifier,
+ )
+}
+
+@Composable
+fun Illustration(
+ resId: Int,
+ resourceType: ResourceType,
+ modifier: Modifier = Modifier
+) {
+ Column(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(horizontal = SettingsDimension.illustrationPadding),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ val illustrationModifier = modifier
+ .sizeIn(
+ maxWidth = SettingsDimension.illustrationMaxWidth,
+ maxHeight = SettingsDimension.illustrationMaxHeight,
+ )
+ .clip(RoundedCornerShape(SettingsDimension.illustrationCornerRadius))
+ .background(color = MaterialTheme.colorScheme.surface)
+
+ when (resourceType) {
+ ResourceType.LOTTIE -> {
+ Lottie(
+ resId = resId,
+ modifier = illustrationModifier,
+ )
+ }
+ ResourceType.IMAGE -> {
+ ImageBox(
+ resId = resId,
+ contentDescription = null,
+ modifier = illustrationModifier,
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
new file mode 100644
index 0000000..f2fe7ad7
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+fun MainSwitchPreference(model: SwitchPreferenceModel) {
+ Surface(
+ modifier = Modifier.padding(SettingsDimension.itemPaddingEnd),
+ color = when (model.checked.value) {
+ true -> MaterialTheme.colorScheme.primaryContainer
+ else -> MaterialTheme.colorScheme.secondaryContainer
+ },
+ shape = SettingsShape.CornerLarge,
+ ) {
+ InternalSwitchPreference(
+ title = model.title,
+ checked = model.checked,
+ changeable = model.changeable,
+ onCheckedChange = model.onCheckedChange,
+ paddingStart = 20.dp,
+ paddingEnd = 20.dp,
+ paddingVertical = 18.dp,
+ )
+ }
+}
+
+@Preview
+@Composable
+fun MainSwitchPreferencePreview() {
+ SettingsTheme {
+ Column {
+ MainSwitchPreference(object : SwitchPreferenceModel {
+ override val title = "Use Dark theme"
+ override val checked = true.toState()
+ override val onCheckedChange: (Boolean) -> Unit = {}
+ })
+ MainSwitchPreference(object : SwitchPreferenceModel {
+ override val title = "Use Dark theme"
+ override val checked = false.toState()
+ override val onCheckedChange: (Boolean) -> Unit = {}
+ })
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index 507421b..77ea3c5 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -67,6 +67,6 @@
Modifier
.padding(horizontal = SettingsDimension.itemPaddingEnd)
.size(width = 1.dp, height = SettingsDimension.itemDividerHeight)
- .background(color = MaterialTheme.colorScheme.onSurface.copy(SettingsOpacity.Disabled))
+ .background(color = MaterialTheme.colorScheme.onSurface.copy(SettingsOpacity.Divider))
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Image.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Image.kt
new file mode 100644
index 0000000..0790bf8
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Image.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.settingslib.spa.widget.ui
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+
+@Composable
+fun ImageBox(
+ resId: Int,
+ contentDescription: String?,
+ modifier: Modifier = Modifier,
+) {
+ Box(
+ modifier = modifier,
+ ) {
+ Image(
+ painter = painterResource(resId),
+ contentDescription = contentDescription,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
new file mode 100644
index 0000000..915c6e2
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.settingslib.spa.widget.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+
+@Composable
+fun Lottie(
+ resId: Int,
+ modifier: Modifier = Modifier,
+) {
+ Box(
+ modifier = modifier,
+ ) {
+ BaseLottie(resId)
+ }
+}
+
+@Composable
+private fun BaseLottie(resId: Int) {
+ val composition by rememberLottieComposition(
+ LottieCompositionSpec.RawRes(resId)
+ )
+ val progress by animateLottieCompositionAsState(
+ composition,
+ iterations = LottieConstants.IterateForever,
+ )
+ LottieAnimation(
+ composition = composition,
+ progress = { progress },
+ )
+}
diff --git a/packages/SettingsLib/Spa/tests/build.gradle b/packages/SettingsLib/Spa/tests/build.gradle
index 5f93a9f..21d696f 100644
--- a/packages/SettingsLib/Spa/tests/build.gradle
+++ b/packages/SettingsLib/Spa/tests/build.gradle
@@ -31,6 +31,9 @@
}
sourceSets {
+ main {
+ res.srcDirs = ["res"]
+ }
androidTest {
kotlin {
srcDir "src"
diff --git a/packages/SettingsLib/Spa/tests/res/drawable/accessibility_captioning_banner.xml b/packages/SettingsLib/Spa/tests/res/drawable/accessibility_captioning_banner.xml
new file mode 100644
index 0000000..6597ffb
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/res/drawable/accessibility_captioning_banner.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="412dp"
+ android:height="300dp"
+ android:viewportWidth="412"
+ android:viewportHeight="300">
+ <path
+ android:pathData="M383.9,300H28.1C12.6,300 0,287.4 0,271.9V28.1C0,12.6 12.6,0 28.1,0h355.8C399.4,0 412,12.6 412,28.1v243.8C412,287.4 399.4,300 383.9,300z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M79.2,179.6h53.6v8.5h-53.6z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M142.5,179.6h30.4v8.5h-30.4z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M79.2,195.5h79.2v8.5h-79.2z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M168.1,195.5h34.1v8.5h-34.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M211.9,195.5h34.1v8.5h-34.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M182.7,179.6h73.1v8.5h-73.1z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M265.5,179.6h26.8v8.5h-26.8z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M302.1,179.6h26.8v8.5h-26.8z"
+ android:fillColor="#1A73E8"/>
+ <path
+ android:pathData="M142.7,67.9h-11.5c-1.6,0 -2.9,1.3 -2.9,2.9H67.8c-7.9,0 -14.4,6.5 -14.4,14.4v132.4c0,7.9 6.5,14.4 14.4,14.4h276.4c7.9,0 14.4,-6.5 14.4,-14.4V85.2c0,-7.9 -6.5,-14.4 -14.4,-14.4H203.1c0,-1.6 -1.3,-2.9 -2.9,-2.9h-28.8c-1.6,0 -2.9,1.3 -2.9,2.9h-23C145.5,69.2 144.3,67.9 142.7,67.9zM344.2,73.7c6.4,0 11.5,5.2 11.5,11.5v132.4c0,6.3 -5.2,11.5 -11.5,11.5H67.8c-6.4,0 -11.5,-5.2 -11.5,-11.5V85.2c0,-6.3 5.2,-11.5 11.5,-11.5H344.2z"
+ android:fillColor="#DADCE0"/>
+</vector>
diff --git a/packages/SettingsLib/Spa/tests/res/raw/accessibility_shortcut_type_triple_tap.json b/packages/SettingsLib/Spa/tests/res/raw/accessibility_shortcut_type_triple_tap.json
new file mode 100644
index 0000000..870e671
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/res/raw/accessibility_shortcut_type_triple_tap.json
@@ -0,0 +1,1959 @@
+{
+ "v": "5.6.5",
+ "fr": 60,
+ "ip": 0,
+ "op": 180,
+ "w": 412,
+ "h": 300,
+ "nm": "Triple_Tap_Screen",
+ "ddd": 0,
+ "assets": [
+ {
+ "id": "comp_0",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": ".white",
+ "cl": "white",
+ "hd": true,
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 15.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -15.4
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -15.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 15.4
+ ]
+ ],
+ "v": [
+ [
+ 178,
+ 150
+ ],
+ [
+ -178,
+ 150
+ ],
+ [
+ -206,
+ 122
+ ],
+ [
+ -206,
+ -122
+ ],
+ [
+ -178,
+ -150
+ ],
+ [
+ 178,
+ -150
+ ],
+ [
+ 206,
+ -122
+ ],
+ [
+ 206,
+ 122
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 1,
+ 1,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 1800,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": ".grey200",
+ "cl": "grey200",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 1.35,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -73.4
+ ],
+ [
+ -73.4,
+ 0
+ ],
+ [
+ 0,
+ 73.4
+ ],
+ [
+ 73.4,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ -73.4,
+ 0
+ ],
+ [
+ 0,
+ 73.4
+ ],
+ [
+ 73.4,
+ 0
+ ],
+ [
+ 0,
+ -73.4
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 1.4,
+ -132.9
+ ],
+ [
+ -131.6,
+ 0
+ ],
+ [
+ 1.3,
+ 132.9
+ ],
+ [
+ 134.3,
+ 0
+ ],
+ [
+ 1.4,
+ -132.9
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ind": 1,
+ "ty": "sh",
+ "ix": 2,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -24.7,
+ -24.8
+ ],
+ [
+ 0,
+ -35
+ ],
+ [
+ 24.8,
+ -24.7
+ ],
+ [
+ 35,
+ 0
+ ],
+ [
+ 24.7,
+ 24.8
+ ],
+ [
+ 0,
+ 35
+ ],
+ [
+ -24.8,
+ 24.7
+ ],
+ [
+ -35,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 35,
+ 0
+ ],
+ [
+ 24.7,
+ 24.7
+ ],
+ [
+ 0,
+ 35
+ ],
+ [
+ -24.7,
+ 24.7
+ ],
+ [
+ -35,
+ 0
+ ],
+ [
+ -24.7,
+ -24.8
+ ],
+ [
+ 0,
+ -35
+ ],
+ [
+ 24.7,
+ -24.7
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 1.4,
+ -130.9
+ ],
+ [
+ 94,
+ -92.5
+ ],
+ [
+ 132.4,
+ 0.1
+ ],
+ [
+ 94,
+ 92.7
+ ],
+ [
+ 1.4,
+ 131.1
+ ],
+ [
+ -91.2,
+ 92.7
+ ],
+ [
+ -129.6,
+ 0
+ ],
+ [
+ -91.2,
+ -92.6
+ ],
+ [
+ 1.4,
+ -130.9
+ ]
+ ],
+ "c": false
+ },
+ "ix": 2
+ },
+ "nm": "Path 2",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.909803926945,
+ 0.917647063732,
+ 0.929411768913,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 2,
+ "ty": 4,
+ "nm": ".grey300",
+ "cl": "grey300",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 205,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ -7.9,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 8
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1.6
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 1.5
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 6.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 6.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ -0.7
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 8,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1.6,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1.9,
+ -1.6
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 6.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -6.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ 0.7
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0.1,
+ 7.9
+ ]
+ ],
+ "v": [
+ [
+ -64,
+ 75.3
+ ],
+ [
+ 69.1,
+ 75.3
+ ],
+ [
+ 83.6,
+ 60.8
+ ],
+ [
+ 83.6,
+ -81
+ ],
+ [
+ 86.5,
+ -83.9
+ ],
+ [
+ 86.5,
+ -100.9
+ ],
+ [
+ 80.7,
+ -105.6
+ ],
+ [
+ 80.7,
+ 60.8
+ ],
+ [
+ 69.1,
+ 72.4
+ ],
+ [
+ -64,
+ 72.4
+ ],
+ [
+ -75.6,
+ 60.8
+ ],
+ [
+ -75.6,
+ -107.3
+ ],
+ [
+ -78.5,
+ -105.2
+ ],
+ [
+ -78.5,
+ 60.9
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.854901969433,
+ 0.86274510622,
+ 0.878431379795,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 1,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 3,
+ "ty": 4,
+ "nm": "cursor 5",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 36,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 39.582,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 44.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 55.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 37.791,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 59,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Ellipse 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 36,
+ "op": 59,
+ "st": -1,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 4,
+ "ty": 4,
+ "nm": "cursor 4",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 22,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 25.58,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 30.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 41.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 23.789,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 45,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Ellipse 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 22,
+ "op": 45,
+ "st": -3,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 5,
+ "ty": 4,
+ "nm": "cursor",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 8,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 11.582,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 16.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 27.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 9.791,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 31,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Ellipse 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 8,
+ "op": 31,
+ "st": -5,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 6,
+ "ty": 0,
+ "nm": "BG_White",
+ "refId": "comp_0",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "w": 412,
+ "h": 300,
+ "ip": 0,
+ "op": 1800,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/res/raw/empty.json b/packages/SettingsLib/Spa/tests/res/raw/empty.json
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/res/raw/empty.json
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/IllustrationTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/IllustrationTest.kt
new file mode 100644
index 0000000..54abec9
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/IllustrationTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.settingslib.spa.widget
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.RawRes
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.SemanticsPropertyKey
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.filterToOne
+import androidx.compose.ui.test.hasAnyAncestor
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.tests.R
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class IllustrationTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val DrawableId = SemanticsPropertyKey<Int>("DrawableResId")
+ private var SemanticsPropertyReceiver.drawableId by DrawableId
+
+ @Test
+ fun image_displayed() {
+ val resId = R.drawable.accessibility_captioning_banner
+ composeTestRule.setContent {
+ Illustration(
+ resId = resId,
+ resourceType = ResourceType.IMAGE,
+ modifier = Modifier.semantics { drawableId = resId }
+ )
+ }
+
+ fun hasDrawable(@DrawableRes id: Int): SemanticsMatcher =
+ SemanticsMatcher.expectValue(DrawableId, id)
+
+ val isIllustrationNode = hasAnyAncestor(hasDrawable(resId))
+ composeTestRule.onAllNodes(hasDrawable(resId))
+ .filterToOne(isIllustrationNode)
+ .assertIsDisplayed()
+ }
+
+ private val RawId = SemanticsPropertyKey<Int>("RawResId")
+ private var SemanticsPropertyReceiver.rawId by RawId
+
+ @Test
+ fun empty_lottie_not_displayed() {
+ val resId = R.raw.empty
+ composeTestRule.setContent {
+ Illustration(
+ resId = resId,
+ resourceType = ResourceType.LOTTIE,
+ modifier = Modifier.semantics { rawId = resId }
+ )
+ }
+
+ fun hasRaw(@RawRes id: Int): SemanticsMatcher =
+ SemanticsMatcher.expectValue(RawId, id)
+
+ val isIllustrationNode = hasAnyAncestor(hasRaw(resId))
+ composeTestRule.onAllNodes(hasRaw(resId))
+ .filterToOne(isIllustrationNode)
+ .assertIsNotDisplayed()
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/MainSwitchPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/MainSwitchPreferenceTest.kt
new file mode 100644
index 0000000..6588cf5
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/MainSwitchPreferenceTest.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TITLE = "Title"
+
+@RunWith(AndroidJUnit4::class)
+class MainSwitchPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ TestMainSwitchPreference(changeable = true)
+ }
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun toggleable_initialStateIsCorrect() {
+ composeTestRule.setContent {
+ TestMainSwitchPreference(changeable = true)
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsOff()
+ }
+
+ @Test
+ fun click_changeable_withEffect() {
+ composeTestRule.setContent {
+ TestMainSwitchPreference(changeable = true)
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+ composeTestRule.onNode(isToggleable()).assertIsOn()
+ }
+
+ @Test
+ fun click_notChangeable_noEffect() {
+ composeTestRule.setContent {
+ TestMainSwitchPreference(changeable = false)
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+ composeTestRule.onNode(isToggleable()).assertIsOff()
+ }
+}
+
+@Composable
+private fun TestMainSwitchPreference(changeable: Boolean) {
+ val checked = rememberSaveable { mutableStateOf(false) }
+ MainSwitchPreference(remember {
+ object : SwitchPreferenceModel {
+ override val title = TITLE
+ override val checked = checked
+ override val changeable = stateOf(changeable)
+ override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ }
+ })
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
index 00eb60b..373b57f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
@@ -12,18 +12,52 @@
val labelCollationKey: CollationKey,
)
+/**
+ * Implement this interface to build an App List.
+ */
interface AppListModel<T : AppRecord> {
+ /**
+ * Returns the spinner options available to the App List.
+ *
+ * Default no spinner will be shown.
+ */
fun getSpinnerOptions(): List<String> = emptyList()
+
+ /**
+ * Loads the extra info for the App List, and generates the [AppRecord] List.
+ */
fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>): Flow<List<T>>
+
+ /**
+ * Filters the [AppRecord] list.
+ *
+ * @return the [AppRecord] list which will be displayed.
+ */
fun filter(userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<T>>): Flow<List<T>>
+ /**
+ * This function is called when the App List's loading is finished and displayed to the user.
+ *
+ * Could do some pre-cache here.
+ */
suspend fun onFirstLoaded(recordList: List<T>) {}
+
+ /**
+ * Gets the comparator to sort the App List.
+ *
+ * Default sorting is based on the app label.
+ */
fun getComparator(option: Int): Comparator<AppEntry<T>> = compareBy(
{ it.labelCollationKey },
{ it.record.app.packageName },
{ it.record.app.uid },
)
+ /**
+ * Gets the summary for the given app record.
+ *
+ * @return null if no summary should be displayed.
+ */
@Composable
fun getSummary(option: Int, record: T): State<String>?
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt
index 6a64620..bb94b33 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt
@@ -46,8 +46,7 @@
.toSet()
}
val flags = PackageManager.ApplicationInfoFlags.of(
- ((if (userInfo.isAdmin) PackageManager.MATCH_ANY_USER else 0) or
- PackageManager.MATCH_DISABLED_COMPONENTS or
+ (PackageManager.MATCH_DISABLED_COMPONENTS or
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
)
val installedApplicationsAsUser =
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
index 0cc497a..e521edd 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
@@ -19,6 +19,9 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
+import android.util.Log
+
+private const val TAG = "PackageManagers"
object PackageManagers {
fun getPackageInfoAsUser(packageName: String, userId: Int): PackageInfo =
@@ -26,4 +29,18 @@
fun getApplicationInfoAsUser(packageName: String, userId: Int): ApplicationInfo =
PackageManager.getApplicationInfoAsUserCached(packageName, 0, userId)
+
+ fun hasRequestPermission(app: ApplicationInfo, permission: String): Boolean {
+ val packageInfo = try {
+ PackageManager.getPackageInfoAsUserCached(
+ app.packageName, PackageManager.GET_PERMISSIONS.toLong(), app.userId
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "getPackageInfoAsUserCached() failed", e)
+ return false
+ }
+ return packageInfo?.requestedPermissions?.let {
+ permission in it
+ } ?: false
+ }
}
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 dc30e79..d537ec2 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
@@ -16,7 +16,9 @@
package com.android.settingslib.spaprivileged.template.app
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
@@ -32,6 +34,7 @@
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
+import com.android.settingslib.spa.widget.ui.Spinner
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
@@ -41,6 +44,7 @@
fun <T : AppRecord> AppListPage(
title: String,
listModel: AppListModel<T>,
+ primaryUserOnly: Boolean = false,
appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
) {
val showSystem = rememberSaveable { mutableStateOf(false) }
@@ -52,16 +56,20 @@
},
) { paddingValues ->
Spacer(Modifier.padding(paddingValues))
- WorkProfilePager { userInfo ->
- // TODO: Add a Spinner here.
- AppList(
- userInfo = userInfo,
- listModel = listModel,
- showSystem = showSystem,
- option = stateOf(0),
- searchQuery = stateOf(""),
- appItem = appItem,
- )
+ WorkProfilePager(primaryUserOnly) { userInfo ->
+ Column(Modifier.fillMaxSize()) {
+ val options = remember { listModel.getSpinnerOptions() }
+ val selectedOption = rememberSaveable { mutableStateOf(0) }
+ Spinner(options, selectedOption.value) { selectedOption.value = it }
+ AppList(
+ userInfo = userInfo,
+ listModel = listModel,
+ showSystem = showSystem,
+ option = selectedOption,
+ searchQuery = stateOf(""),
+ appItem = appItem,
+ )
+ }
}
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
new file mode 100644
index 0000000..5290bec
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
@@ -0,0 +1,32 @@
+package com.android.settingslib.spaprivileged.template.app
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+
+@Composable
+fun <T : AppRecord> AppListSwitchItem(
+ itemModel: AppListItemModel<T>,
+ onClick: () -> Unit,
+ checked: State<Boolean?>,
+ changeable: State<Boolean>,
+ onCheckedChange: ((newChecked: Boolean) -> Unit)?,
+) {
+ TwoTargetSwitchPreference(
+ model = remember {
+ object : SwitchPreferenceModel {
+ override val title = itemModel.label
+ override val summary = itemModel.summary
+ override val checked = checked
+ override val changeable = changeable
+ override val onCheckedChange = onCheckedChange
+ }
+ },
+ icon = { AppIcon(itemModel.record.app, SettingsDimension.appIconItemSize) },
+ onClick = onClick,
+ )
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index eb9ce5e..d4d8ea4 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -26,8 +26,12 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.core.os.bundleOf
import androidx.navigation.NavType
import androidx.navigation.navArgument
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.widget.preference.SwitchPreference
@@ -37,27 +41,37 @@
import com.android.settingslib.spaprivileged.model.app.toRoute
import kotlinx.coroutines.Dispatchers
-private const val NAME = "TogglePermissionAppInfoPage"
+private const val ENTRY_NAME = "AllowControl"
private const val PERMISSION = "permission"
private const val PACKAGE_NAME = "packageName"
private const val USER_ID = "userId"
+private const val PAGE_NAME = "TogglePermissionAppInfoPage"
+private val PAGE_PARAMETER = listOf(
+ navArgument(PERMISSION) { type = NavType.StringType },
+ navArgument(PACKAGE_NAME) { type = NavType.StringType },
+ navArgument(USER_ID) { type = NavType.IntType },
+)
internal class TogglePermissionAppInfoPageProvider(
private val appListTemplate: TogglePermissionAppListTemplate,
) : SettingsPageProvider {
- override val name = NAME
+ override val name = PAGE_NAME
- override val arguments = listOf(
- navArgument(PERMISSION) { type = NavType.StringType },
- navArgument(PACKAGE_NAME) { type = NavType.StringType },
- navArgument(USER_ID) { type = NavType.IntType },
- )
+ override val parameter = PAGE_PARAMETER
+
+ override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
+ val owner = SettingsPage.create(name, parameter, arguments)
+ val entryList = mutableListOf<SettingsEntry>()
+ entryList.add(
+ SettingsEntryBuilder.create(ENTRY_NAME, owner).setIsAllowSearch(false).build()
+ )
+ return entryList
+ }
@Composable
override fun Page(arguments: Bundle?) {
- checkNotNull(arguments)
- val permissionType = checkNotNull(arguments.getString(PERMISSION))
- val packageName = checkNotNull(arguments.getString(PACKAGE_NAME))
+ val permissionType = arguments?.getString(PERMISSION)!!
+ val packageName = arguments.getString(PACKAGE_NAME)!!
val userId = arguments.getInt(USER_ID)
val listModel = appListTemplate.rememberModel(permissionType)
TogglePermissionAppInfoPage(listModel, packageName, userId)
@@ -66,7 +80,12 @@
companion object {
@Composable
internal fun navigator(permissionType: String, app: ApplicationInfo) =
- navigator(route = "$NAME/$permissionType/${app.toRoute()}")
+ navigator(route = "$PAGE_NAME/$permissionType/${app.toRoute()}")
+
+ internal fun buildPageId(permissionType: String): SettingsPage {
+ return SettingsPage.create(
+ PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType))
+ }
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index 3cc5854..4c748b8 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -21,6 +21,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.framework.util.asyncMapItem
@@ -29,21 +30,51 @@
import com.android.settingslib.spaprivileged.model.app.AppRecord
import kotlinx.coroutines.flow.Flow
+/**
+ * Implement this interface to build an App List which toggles a permission on / off.
+ */
interface TogglePermissionAppListModel<T : AppRecord> {
val pageTitleResId: Int
val switchTitleResId: Int
val footerResId: Int
+ /**
+ * Loads the extra info for the App List, and generates the [AppRecord] List.
+ *
+ * Default is implemented by [transformItem]
+ */
fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>): Flow<List<T>> =
appListFlow.asyncMapItem(::transformItem)
+ /**
+ * Loads the extra info for one app, and generates the [AppRecord].
+ *
+ * This must be implemented, because when show the App Info page for single app, this will be
+ * used instead of [transform].
+ */
fun transformItem(app: ApplicationInfo): T
+
+ /**
+ * Filters the [AppRecord] list.
+ *
+ * @return the [AppRecord] list which will be displayed.
+ */
fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<T>>): Flow<List<T>>
+ /**
+ * Gets whether the permission is allowed for the given app.
+ */
@Composable
fun isAllowed(record: T): State<Boolean?>
+ /**
+ * Gets whether the permission on / off is changeable for the given app.
+ */
fun isChangeable(record: T): Boolean
+
+ /**
+ * Sets whether the permission is allowed for the given app.
+ */
fun setAllowed(record: T, newAllowed: Boolean)
}
@@ -52,6 +83,10 @@
fun createModel(context: Context): TogglePermissionAppListModel<out AppRecord>
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return TogglePermissionAppListPageProvider.buildInjectEntry(permissionType)
+ }
+
@Composable
fun EntryItem() {
val listModel = rememberContext(::createModel)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index f2eb962..f91a34a 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -25,32 +25,54 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.core.os.bundleOf
import androidx.navigation.NavType
import androidx.navigation.navArgument
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.util.getStringArg
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import kotlinx.coroutines.flow.Flow
-private const val NAME = "TogglePermissionAppList"
+private const val ENTRY_NAME = "AppList"
private const val PERMISSION = "permission"
+private const val PAGE_NAME = "TogglePermissionAppList"
+private val PAGE_PARAMETER = listOf(
+ navArgument(PERMISSION) { type = NavType.StringType },
+)
internal class TogglePermissionAppListPageProvider(
private val appListTemplate: TogglePermissionAppListTemplate,
) : SettingsPageProvider {
- override val name = NAME
+ override val name = PAGE_NAME
- override val arguments = listOf(
- navArgument(PERMISSION) { type = NavType.StringType },
- )
+ override val parameter = PAGE_PARAMETER
+
+ override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
+ val permissionType = parameter.getStringArg(PERMISSION, arguments)!!
+ val appListPage = SettingsPage.create(name, parameter, arguments)
+ val appInfoPage = TogglePermissionAppInfoPageProvider.buildPageId(permissionType)
+ val entryList = mutableListOf<SettingsEntry>()
+ // TODO: add more categories, such as personal, work, cloned, etc.
+ for (category in listOf("personal")) {
+ entryList.add(
+ SettingsEntryBuilder.createLinkFrom("${ENTRY_NAME}_$category", appListPage)
+ .setLink(toPage = appInfoPage)
+ .setIsAllowSearch(false)
+ .build()
+ )
+ }
+ return entryList
+ }
@Composable
override fun Page(arguments: Bundle?) {
- checkNotNull(arguments)
- val permissionType = checkNotNull(arguments.getString(PERMISSION))
- TogglePermissionAppList(permissionType)
+ TogglePermissionAppList(arguments?.getString(PERMISSION)!!)
}
@Composable
@@ -80,9 +102,16 @@
*
* Expose route to enable enter from non-SPA pages.
*/
- internal fun getRoute(permissionType: String) = "$NAME/$permissionType"
+ internal fun getRoute(permissionType: String) = "$PAGE_NAME/$permissionType"
+
@Composable
internal fun navigator(permissionType: String) = navigator(route = getRoute(permissionType))
+
+ internal fun buildInjectEntry(permissionType: String): SettingsEntryBuilder {
+ val appListPage = SettingsPage.create(
+ PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType))
+ return SettingsEntryBuilder.createInject(appListPage).setIsAllowSearch(false)
+ }
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
index aa5ccf1..a76c438 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
@@ -26,11 +26,16 @@
import com.android.settingslib.spaprivileged.model.enterprise.EnterpriseRepository
@Composable
-fun WorkProfilePager(content: @Composable (userInfo: UserInfo) -> Unit) {
+fun WorkProfilePager(
+ primaryUserOnly: Boolean = false,
+ content: @Composable (userInfo: UserInfo) -> Unit,
+) {
val context = LocalContext.current
val profiles = remember {
val userManager = checkNotNull(context.getSystemService(UserManager::class.java))
- userManager.getProfiles(UserHandle.myUserId())
+ userManager.getProfiles(UserHandle.myUserId()).filter { userInfo ->
+ !primaryUserOnly || userInfo.isPrimary
+ }
}
val titles = remember {
val enterpriseRepository = EnterpriseRepository(context)
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 4a67adb..494ec1e 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g> ይቀራል"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት ባለበት ቆሟል"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ኃይል በዝግታ በመሙላት ላይ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"በገመድ-አልባ ኃይል በመሙላት ላይ"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ባትሪ እየሞላ አይደለም"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"ተገናኝቷል፣ ኃይል በመሙላት ላይ አይደለም"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ባትሪ ሞልቷል"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 3683014..d7d09dd 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিং পজ কৰা হৈছে"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"লাহে লাহে চাৰ্জ হৈছে"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"বেতাঁৰৰ মাধ্যমেৰে চাৰ্জ হৈ আছে"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"চাৰ্জ কৰি থকা হৈছে"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"চ্চাৰ্জ কৰা নাই"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"সংযোগ হৈ আছে, চাৰ্জ হৈ থকা নাই"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"চাৰ্জ হ’ল"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 66baea6..2f1b7d7 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Да поўнай зарадкі засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – да поўнай зарадкі засталося: <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - Зарадка прыпынена"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Павольная зарадка"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бесправадная зарадка"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Ідзе зарадка"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не зараджаецца"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Падключана, не зараджаецца"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Зараджаны"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 833fe15..9082210 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -408,8 +408,7 @@
<string name="force_resizable_activities_summary" msgid="2490382056981583062">"Die Größe aller Aktivitäten darf, ungeachtet der Manifestwerte, für die Mehrfensterdarstellung angepasst werden"</string>
<string name="enable_freeform_support" msgid="7599125687603914253">"Freiform-Fenster zulassen"</string>
<string name="enable_freeform_support_summary" msgid="1822862728719276331">"Unterstützung für experimentelle Freiform-Fenster aktivieren"</string>
- <!-- no translation found for desktop_mode (2389067840550544462) -->
- <skip />
+ <string name="desktop_mode" msgid="2389067840550544462">"Desktopmodus"</string>
<string name="local_backup_password_title" msgid="4631017948933578709">"Passwort für Desktop-Sicherung"</string>
<string name="local_backup_password_summary_none" msgid="7646898032616361714">"Vollständige Desktop-Sicherungen sind momentan nicht passwortgeschützt"</string>
<string name="local_backup_password_summary_change" msgid="1707357670383995567">"Zum Ändern oder Entfernen des Passworts für vollständige Desktop-Sicherungen tippen"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 243ecaa..c0b8a27 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -668,7 +668,7 @@
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"اگر <xliff:g id="SWITCHAPP">%1$s</xliff:g> را همهفرستی کنید یا خروجی را تغییر دهید، همهفرستی کنونی متوقف خواهد شد"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"همهفرستی <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
<string name="bt_le_audio_broadcast_dialog_different_output" msgid="2638402023060391333">"تغییر خروجی"</string>
- <string name="back_navigation_animation" msgid="8105467568421689484">"پویانماییهای اشاره برگشت پیشبینانه"</string>
- <string name="back_navigation_animation_summary" msgid="741292224121599456">"پویانماییهای سیستم را برای اشاره برگشت پیشبینانه فعال کنید."</string>
+ <string name="back_navigation_animation" msgid="8105467568421689484">"پویانماییهای اشاره برگشت پیشگویانه"</string>
+ <string name="back_navigation_animation_summary" msgid="741292224121599456">"پویانماییهای سیستم را برای اشاره برگشت پیشگویانه فعال کنید."</string>
<string name="back_navigation_animation_dialog" msgid="8696966520944625596">"این تنظیم پویانماییهای سیستم را برای پویانمایی اشاره برگشت پیشبینانه فعال میکند. این تنظیم مستلزم تنظیم شدن enableOnBackInvokedCallback مربوط به هر برنامه روی صحیح در فایل مانیفست است."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index e268331..66373b5 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kunnes täynnä"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus on keskeytetty"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hidas lataus"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Langaton lataus"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Ladataan"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei laturissa"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Yhdistetty, ei ladata"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Ladattu"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index a925bd0..f1acddae 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -211,15 +211,17 @@
<string name="tts_general_section_title" msgid="8919671529502364567">"Général"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Réinitialiser la hauteur de la voix du texte"</string>
<string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Rétablir la hauteur normale à laquelle le texte est énoncé."</string>
- <!-- no translation found for tts_rate_entries:0 (4563475121751694801) -->
- <!-- no translation found for tts_rate_entries:1 (6323184326270638754) -->
- <!-- no translation found for tts_rate_entries:2 (2569200872125313769) -->
- <!-- no translation found for tts_rate_entries:3 (9010794089704942570) -->
- <!-- no translation found for tts_rate_entries:4 (4634010129655810634) -->
- <!-- no translation found for tts_rate_entries:5 (8929490998534497543) -->
- <!-- no translation found for tts_rate_entries:6 (8442352376763286772) -->
- <!-- no translation found for tts_rate_entries:7 (4446831566506165093) -->
- <!-- no translation found for tts_rate_entries:8 (6946761421234586000) -->
+ <string-array name="tts_rate_entries">
+ <item msgid="4563475121751694801">"60 %"</item>
+ <item msgid="6323184326270638754">"80 %"</item>
+ <item msgid="2569200872125313769">"100 %"</item>
+ <item msgid="9010794089704942570">"150 %"</item>
+ <item msgid="4634010129655810634">"200 %"</item>
+ <item msgid="8929490998534497543">"250 %"</item>
+ <item msgid="8442352376763286772">"300 %"</item>
+ <item msgid="4446831566506165093">"350 %"</item>
+ <item msgid="6946761421234586000">"400 %"</item>
+ </string-array>
<string name="choose_profile" msgid="343803890897657450">"Sélectionnez un profil"</string>
<string name="category_personal" msgid="6236798763159385225">"Personnel"</string>
<string name="category_work" msgid="4014193632325996115">"Professionnel"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8c99837..30ef44e 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar a carga)"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g>: a carga está en pausa"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Cargando lentamente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sen fíos"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Cargando"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Non se está cargando"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado, sen cargar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
@@ -670,7 +668,7 @@
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"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>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Emitir contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
<string name="bt_le_audio_broadcast_dialog_different_output" msgid="2638402023060391333">"Cambiar de saída"</string>
- <string name="back_navigation_animation" msgid="8105467568421689484">"Animacións para o xesto preditivo de volver atrás"</string>
- <string name="back_navigation_animation_summary" msgid="741292224121599456">"Activa as animacións do sistema para o xesto preditivo de volver atrás."</string>
- <string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Esta opción de configuración activa as animacións do sistema para o xesto preditivo de volver atrás. É preciso definir enableOnBackInvokedCallback como True (verdadeiro) para cada aplicación no ficheiro de manifesto."</string>
+ <string name="back_navigation_animation" msgid="8105467568421689484">"Animacións de retroceso preditivo"</string>
+ <string name="back_navigation_animation_summary" msgid="741292224121599456">"Activa as animacións do sistema para o retroceso preditivo."</string>
+ <string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Esta opción de configuración activa as animacións xestuais preditivas. É preciso definir enableOnBackInvokedCallback como True (verdadeiro) para cada aplicación no ficheiro de manifesto."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 2e7b30b..4affd4f 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումը դադարեցված է"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Դանդաղ լիցքավորում"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Անլար լիցքավորում"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Լիցքավորում"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Չի լիցքավորվում"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Միացված է, չի լիցքավորվում"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Լիցքավորված է"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 42ac063..b541d50 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> fram að fullri hleðslu"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> fram að fullri hleðslu"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hlé var gert á hleðslu"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hæg hleðsla"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hleður þráðlaust"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Í hleðslu"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ekki í hleðslu"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tengt, ekki í hleðslu"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Fullhlaðin"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index d80d708..7bfa1c4 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបពេញ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបពេញ"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - ការសាកថ្មត្រូវបានផ្អាក"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"មិនស្គាល់"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងសាកថ្ម"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"កំពុងសាកថ្មយឺត"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"កំពុងសាកថ្មឥតខ្សែ"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"កំពុងសាកថ្ម"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"មិនកំពុងសាកថ្ម"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"បានភ្ជាប់ មិនកំពុងសាកថ្ម"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"បានសាកថ្មពេញ"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index ed7cdd2..d5dc291 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -195,7 +195,7 @@
<string name="tts_default_lang_summary" msgid="9042620014800063470">"Текстти окуй турган тилди тандоо"</string>
<string name="tts_play_example_title" msgid="1599468547216481684">"Үлгүнү угуу"</string>
<string name="tts_play_example_summary" msgid="634044730710636383">"Кепти синтездөөнүн кыскача көргөзмөсүн ойнотуу"</string>
- <string name="tts_install_data_title" msgid="1829942496472751703">"Үн дайындарын орнотуу"</string>
+ <string name="tts_install_data_title" msgid="1829942496472751703">"Үнгө байланыштуу нерселерди орнотуу"</string>
<string name="tts_install_data_summary" msgid="3608874324992243851">"Кеп синтезине керектүү үн дайындарын орнотуңуз"</string>
<string name="tts_engine_security_warning" msgid="3372432853837988146">"Бул кепти синтездөө каражаты бардык айтыла турган текстти, анын ичинде сырсөздөр жана насыя карточкасынын номери сыяктуу жеке маалыматты, топтошу мүмкүн. Ал <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> каражатынан алынат. Бул кепти синтездөө каражаты колдонулсунбу?"</string>
<string name="tts_engine_network_required" msgid="8722087649733906851">"Бул тилде кеп синтезаторун иштетүү үчүн Интернетке туташуу керек."</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index e0efe21..e87422d 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -201,9 +201,9 @@
<string name="tts_engine_network_required" msgid="8722087649733906851">"या भाषेस टेक्स्ट टू स्पीचसाठी एका नेटवर्क कनेक्शनची आवश्यकता आहे."</string>
<string name="tts_default_sample_string" msgid="6388016028292967973">"हे उच्चार संश्लेषणाचे एक उदाहरण आहे"</string>
<string name="tts_status_title" msgid="8190784181389278640">"डीफॉल्ट भाषा स्थिती"</string>
- <string name="tts_status_ok" msgid="8583076006537547379">"<xliff:g id="LOCALE">%1$s</xliff:g> पूर्णपणे समर्थित आहे"</string>
+ <string name="tts_status_ok" msgid="8583076006537547379">"<xliff:g id="LOCALE">%1$s</xliff:g> ला पूर्ण सपोर्ट आहे"</string>
<string name="tts_status_requires_network" msgid="8327617638884678896">"<xliff:g id="LOCALE">%1$s</xliff:g> ला नेटवर्क कनेक्शनची आवश्यकता आहे"</string>
- <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> समर्थित नाही"</string>
+ <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> ला सपोर्ट नाही"</string>
<string name="tts_status_checking" msgid="8026559918948285013">"तपासत आहे..."</string>
<string name="tts_engine_settings_title" msgid="7849477533103566291">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> साठी सेटिंग्ज"</string>
<string name="tts_engine_settings_button" msgid="477155276199968948">"इंजीन सेटिंग्ज लाँच करा"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 98bff9e..1384d5b 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do pełnego naładowania"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie zostało wstrzymane"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Wolne ładowanie"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ładowanie bezprzewodowe"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Ładowanie"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nie podłączony"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Podłączono, brak ładowania"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Naładowana"</string>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index 41a5965..987b9c3 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -120,13 +120,13 @@
<item msgid="8946330945963372966">"96,0 kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
- <item msgid="2574107108483219051">"Folosiți selectarea sistemului (prestabilit)"</item>
+ <item msgid="2574107108483219051">"Folosește selectarea sistemului (prestabilit)"</item>
<item msgid="4671992321419011165">"16 biți/eșantion"</item>
<item msgid="1933898806184763940">"24 biți/eșantion"</item>
<item msgid="1212577207279552119">"32 biți/eșantion"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
- <item msgid="9196208128729063711">"Folosiți selectarea sistemului (prestabilit)"</item>
+ <item msgid="9196208128729063711">"Folosește selectarea sistemului (prestabilit)"</item>
<item msgid="1084497364516370912">"16 biți/eșantion"</item>
<item msgid="2077889391457961734">"24 biți/eșantion"</item>
<item msgid="3836844909491316925">"32 biți/eșantion"</item>
@@ -137,7 +137,7 @@
<item msgid="927546067692441494">"Stereo"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
- <item msgid="1997302811102880485">"Folosiți selectarea sistemului (prestabilit)"</item>
+ <item msgid="1997302811102880485">"Folosește selectarea sistemului (prestabilit)"</item>
<item msgid="8005696114958453588">"Mono"</item>
<item msgid="1333279807604675720">"Stereo"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index e9f4030..8b6259c 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -74,14 +74,14 @@
<string name="private_dns_broken" msgid="1984159464346556931">"Serverul DNS privat nu poate fi accesat"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexiune limitată"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Fără conexiune la internet"</string>
- <string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Trebuie să vă conectați"</string>
+ <string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Trebuie să te conectezi"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Punctul de acces este temporar plin"</string>
<string name="connected_via_carrier" msgid="1968057009076191514">"Conectată prin %1$s"</string>
<string name="available_via_carrier" msgid="465598683092718294">"Disponibilă prin %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Se deschide <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nu s-a putut conecta"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Se finalizează înscrierea…"</string>
- <string name="osu_sign_up_failed" msgid="5605453599586001793">"Nu s-a putut finaliza înscrierea. Atingeți pentru a încerca din nou."</string>
+ <string name="osu_sign_up_failed" msgid="5605453599586001793">"Nu s-a putut finaliza înscrierea. Atinge pentru a încerca din nou."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Înscrierea a fost finalizată. Se conectează…"</string>
<string name="speed_label_very_slow" msgid="8526005255731597666">"Foarte lentă"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lentă"</string>
@@ -118,7 +118,7 @@
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispozitiv de intrare"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acces la internet"</string>
<string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Acces la agendă și istoricul apelurilor"</string>
- <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Folosiți pentru accesul la agendă și istoricul apelurilor"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Folosește pentru accesul la agendă și istoricul apelurilor"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Distribuirea conexiunii la internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesaje text"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acces la SIM"</string>
@@ -138,17 +138,17 @@
<string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Conectat la dispoz. pt. acces internet"</string>
<string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"Acces la internet local"</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="7422039765025340313">"Folosește pentru acces la internet"</string>
- <string name="bluetooth_map_profile_summary_use_for" msgid="4453622103977592583">"Utilizați pentru hartă"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="4453622103977592583">"Folosește pentru hartă"</string>
<string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"Folosiți pentru acces la SIM"</string>
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"Folosește pentru profilul pentru conținut media audio"</string>
<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">"Utilizați pentru introducere date"</string>
<string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosiți pentru aparatele auditive"</string>
- <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Folosiți pentru LE_AUDIO"</string>
- <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Asociați"</string>
- <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"CONECTAȚI"</string>
- <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Anulați"</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>
+ <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Anulează"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Asocierea dispozitivelor îți permite accesul la persoanele de contact și la istoricul apelurilor când dispozitivul este conectat."</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Nu s-a putut împerechea cu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Nu s-a putut asocia cu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> din cauza unui cod PIN sau a unei chei de acces incorecte."</string>
@@ -194,10 +194,10 @@
<string name="tts_lang_not_selected" msgid="7927823081096056147">"Nu ai selectat limba"</string>
<string name="tts_default_lang_summary" msgid="9042620014800063470">"Setează vocea caracteristică limbii pentru textul vorbit"</string>
<string name="tts_play_example_title" msgid="1599468547216481684">"Ascultă un exemplu"</string>
- <string name="tts_play_example_summary" msgid="634044730710636383">"Redați o demonstrație scurtă a sintetizării vorbirii"</string>
- <string name="tts_install_data_title" msgid="1829942496472751703">"Instalați date vocale"</string>
- <string name="tts_install_data_summary" msgid="3608874324992243851">"Instalați datele vocale necesare pentru sintetizarea vorbirii"</string>
- <string name="tts_engine_security_warning" msgid="3372432853837988146">"Acest motor de sintetizare a vorbirii poate culege în întregime textul vorbit, inclusiv datele personale cum ar fi parolele și numerele cărților de credit. Metoda provine de la motorul <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Permiteți utilizarea acestui motor de sintetizare a vorbirii?"</string>
+ <string name="tts_play_example_summary" msgid="634044730710636383">"Redă o demonstrație scurtă a sintetizării vorbirii"</string>
+ <string name="tts_install_data_title" msgid="1829942496472751703">"Instalează date vocale"</string>
+ <string name="tts_install_data_summary" msgid="3608874324992243851">"Instalează datele vocale necesare pentru sintetizarea vorbirii"</string>
+ <string name="tts_engine_security_warning" msgid="3372432853837988146">"Acest motor de sintetizare a vorbirii poate culege în întregime textul vorbit, inclusiv datele personale cum ar fi parolele și numerele cărților de credit. Metoda provine de la motorul <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Permiți utilizarea acestui motor de sintetizare a vorbirii?"</string>
<string name="tts_engine_network_required" msgid="8722087649733906851">"Pentru rezultatul transformării textului în vorbire pentru această limbă este necesară o conexiune de rețea care să funcționeze."</string>
<string name="tts_default_sample_string" msgid="6388016028292967973">"Acesta este un exemplu de sintetizare a vorbirii"</string>
<string name="tts_status_title" msgid="8190784181389278640">"Starea limbii prestabilite"</string>
@@ -210,7 +210,7 @@
<string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Motor preferat"</string>
<string name="tts_general_section_title" msgid="8919671529502364567">"Preferințe generale"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Resetează tonalitatea vorbirii"</string>
- <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Resetați tonalitatea cu care se rostește textul în mod prestabilit."</string>
+ <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Resetează tonalitatea cu care se rostește textul în mod prestabilit."</string>
<string-array name="tts_rate_entries">
<item msgid="4563475121751694801">"60 %"</item>
<item msgid="6323184326270638754">"80 %"</item>
@@ -227,7 +227,7 @@
<string name="category_work" msgid="4014193632325996115">"Serviciu"</string>
<string name="development_settings_title" msgid="140296922921597393">"Opțiuni pentru dezvoltatori"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Activează opțiunile pentru dezvoltatori"</string>
- <string name="development_settings_summary" msgid="8718917813868735095">"Setați opțiuni pentru dezvoltarea aplicației"</string>
+ <string name="development_settings_summary" msgid="8718917813868735095">"Setează opțiuni pentru dezvoltarea aplicației"</string>
<string name="development_settings_not_available" msgid="355070198089140951">"Opțiunile de dezvoltator nu sunt disponibile pentru acest utilizator"</string>
<string name="vpn_settings_not_available" msgid="2894137119965668920">"Setările VPN nu sunt disponibile pentru acest utilizator"</string>
<string name="tethering_settings_not_available" msgid="266821736434699780">"Setările pentru tethering nu sunt disponibile pentru acest utilizator"</string>
@@ -240,14 +240,14 @@
<string name="adb_wireless_error" msgid="721958772149779856">"Eroare"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Remedierea erorilor wireless"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Activează remedierea erorilor wireless pentru a vedea și a folosi dispozitivele disponibile"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Asociați dispozitivul folosind codul QR"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Asociază dispozitivul folosind codul QR"</string>
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Asociază dispozitive noi folosind scannerul de coduri QR"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"Asociază dispozitivul folosind codul de conectare"</string>
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Asociază dispozitive noi folosind codul din șase cifre"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"Dispozitive asociate"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Conectat"</string>
<string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Detalii despre dispozitiv"</string>
- <string name="adb_device_forget" msgid="193072400783068417">"Ștergeți"</string>
+ <string name="adb_device_forget" msgid="193072400783068417">"Șterge"</string>
<string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Amprenta pentru dispozitiv: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Conectare nereușită"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Asigură-te că ai conectat <xliff:g id="DEVICE_NAME">%1$s</xliff:g> la rețeaua corectă"</string>
@@ -261,17 +261,17 @@
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adresa IP și portul"</string>
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scanează codul QR"</string>
<string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Asociază dispozitivul prin Wi-Fi scanând un cod QR"</string>
- <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Conectați-vă la o rețea Wi-Fi"</string>
+ <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Conectează-te la o rețea Wi-Fi"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, remedierea erorilor, dev"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Comandă rapidă pentru raportul de erori"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Afișează un buton în meniul de pornire pentru a realiza un raport de erori"</string>
<string name="keep_screen_on" msgid="1187161672348797558">"Activ permanent"</string>
<string name="keep_screen_on_summary" msgid="1510731514101925829">"Ecranul nu va fi inactiv pe durata încărcării"</string>
<string name="bt_hci_snoop_log" msgid="7291287955649081448">"Activează jurnalul de examinare HCI Bluetooth"</string>
- <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Înregistrați pachetele Bluetooth. (Comutați Bluetooth după modificarea setării)"</string>
+ <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Înregistrează pachetele Bluetooth. (Comutați Bluetooth după modificarea setării)"</string>
<string name="oem_unlock_enable" msgid="5334869171871566731">"Deblocarea OEM"</string>
- <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Permiteți deblocarea bootloaderului"</string>
- <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Permiteți deblocarea OEM?"</string>
+ <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Permite deblocarea bootloaderului"</string>
+ <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Permiți deblocarea OEM?"</string>
<string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"AVERTISMENT: funcțiile de protecție a dispozitivului nu vor funcționa pe acest dispozitiv cât timp setarea este activată."</string>
<string name="mock_location_app" msgid="6269380172542248304">"Selectează aplicația pentru locația de testare"</string>
<string name="mock_location_app_not_set" msgid="6972032787262831155">"Nicio aplicație setată pentru locația de testare"</string>
@@ -284,7 +284,7 @@
<string name="mobile_data_always_on" msgid="8275958101875563572">"Date mobile permanent active"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Accelerare hardware pentru tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afișează dispozitivele Bluetooth fără nume"</string>
- <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Dezactivați volumul absolut"</string>
+ <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Dezactivează volumul absolut"</string>
<string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activează Gabeldorsche"</string>
<string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string>
<string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectează versiunea AVRCP pentru Bluetooth"</string>
@@ -303,7 +303,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"Declanșați codecul LDAC audio pentru Bluetooth\nSelecție: calitatea redării"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Transmitere în flux: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="7887550926056143018">"DNS privat"</string>
- <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Selectați modul DNS privat"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Selectează modul DNS privat"</string>
<string name="private_dns_mode_off" msgid="7065962499349997041">"Dezactivat"</string>
<string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"Automat"</string>
<string name="private_dns_mode_provider" msgid="3619040641762557028">"Nume de gazdă al furnizorului de DNS privat"</string>
@@ -321,18 +321,18 @@
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"Când nu mai monitorizăm folosind jurnalul permanent, trebuie să ștergem datele de jurnal aflate pe dispozitivul tău."</string>
<string name="select_logpersist_title" msgid="447071974007104196">"Stochează date jurnal permanent pe dispozitiv"</string>
<string name="select_logpersist_dialog_title" msgid="7745193591195485594">"Selectează zonele-tampon ale jurnalului de stocat permanent pe dispozitiv"</string>
- <string name="select_usb_configuration_title" msgid="6339801314922294586">"Selectați configurația USB"</string>
- <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"Selectați configurația USB"</string>
- <string name="allow_mock_location" msgid="2102650981552527884">"Permiteți locațiile fictive"</string>
+ <string name="select_usb_configuration_title" msgid="6339801314922294586">"Selectează configurația USB"</string>
+ <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"Selectează configurația USB"</string>
+ <string name="allow_mock_location" msgid="2102650981552527884">"Permite locațiile fictive"</string>
<string name="allow_mock_location_summary" msgid="179780881081354579">"Permite locațiile fictive"</string>
<string name="debug_view_attributes" msgid="3539609843984208216">"Activează inspectarea atributelor de vizualizare"</string>
<string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Păstrați întotdeauna conexiunea de date mobile activată, chiar și atunci când funcția Wi‑Fi este activată (pentru comutarea rapidă între rețele)."</string>
- <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Folosiți accelerarea hardware pentru tethering, dacă este disponibilă"</string>
+ <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Folosește accelerarea hardware pentru tethering, dacă este disponibilă"</string>
<string name="adb_warning_title" msgid="7708653449506485728">"Permiteți remedierea erorilor prin USB?"</string>
<string name="adb_warning_message" msgid="8145270656419669221">"Remedierea erorilor prin USB are exclusiv scopuri de dezvoltare. Utilizați-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
- <string name="adbwifi_warning_title" msgid="727104571653031865">"Permiteți remedierea erorilor wireless?"</string>
- <string name="adbwifi_warning_message" msgid="8005936574322702388">"Remedierea erorilor wireless are exclusiv scopuri de dezvoltare. Folosiți-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
- <string name="adb_keys_warning_message" msgid="2968555274488101220">"Revocați accesul la remedierea erorilor prin USB de pe toate computerele pe care le-ați autorizat anterior?"</string>
+ <string name="adbwifi_warning_title" msgid="727104571653031865">"Permiți remedierea erorilor wireless?"</string>
+ <string name="adbwifi_warning_message" msgid="8005936574322702388">"Remedierea erorilor wireless are exclusiv scopuri de dezvoltare. Folosește-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
+ <string name="adb_keys_warning_message" msgid="2968555274488101220">"Revoci accesul la remedierea erorilor prin USB de pe toate computerele pe care le-ai autorizat anterior?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Permiți setările pentru dezvoltare?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Aceste setări sunt destinate exclusiv utilizării pentru dezvoltare. Din cauza lor, este posibil ca dispozitivul tău și aplicațiile de pe acesta să nu mai funcționeze sau să funcționeze necorespunzător."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verificați aplicațiile prin USB"</string>
@@ -347,9 +347,9 @@
<string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Configurează verif. HDCP"</string>
<string name="debug_debugging_category" msgid="535341063709248842">"Depanare"</string>
<string name="debug_app" msgid="8903350241392391766">"Selectează aplicația de depanare"</string>
- <string name="debug_app_not_set" msgid="1934083001283807188">"Nu ați setat o aplicație de depanare"</string>
+ <string name="debug_app_not_set" msgid="1934083001283807188">"Nu ai setat o aplicație de depanare"</string>
<string name="debug_app_set" msgid="6599535090477753651">"Aplicație de depanare: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="select_application" msgid="2543228890535466325">"Selectați o aplicație"</string>
+ <string name="select_application" msgid="2543228890535466325">"Selectează o aplicație"</string>
<string name="no_application" msgid="9038334538870247690">"Niciuna"</string>
<string name="wait_for_debugger" msgid="7461199843335409809">"Așteaptă depanatorul"</string>
<string name="wait_for_debugger_summary" msgid="6846330006113363286">"Înaintea executării, aplicația așteaptă atașarea depanatorului"</string>
@@ -362,8 +362,8 @@
<string name="strict_mode_summary" msgid="1838248687233554654">"Iluminare intermitentă la operații lungi pe firul principal"</string>
<string name="pointer_location" msgid="7516929526199520173">"Locația indicatorului"</string>
<string name="pointer_location_summary" msgid="957120116989798464">"Suprapunere care indică date curente pt. atingeri"</string>
- <string name="show_touches" msgid="8437666942161289025">"Afișați atingerile"</string>
- <string name="show_touches_summary" msgid="3692861665994502193">"Afișați feedbackul vizual pentru 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_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>
@@ -371,14 +371,14 @@
<string name="show_hw_layers_updates" msgid="5268370750002509767">"Actualiz. strat. hardware"</string>
<string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Straturile hardware clipesc verde la actualizare"</string>
<string name="debug_hw_overdraw" msgid="8944851091008756796">"Remediază suprapunerea"</string>
- <string name="disable_overlays" msgid="4206590799671557143">"Dezactivați suprapun. HW"</string>
+ <string name="disable_overlays" msgid="4206590799671557143">"Dezactiv. suprapuneri HW"</string>
<string name="disable_overlays_summary" msgid="1954852414363338166">"Folosește mereu GPU pentru compunerea ecranului"</string>
<string name="simulate_color_space" msgid="1206503300335835151">"Simulează spațiu culoare"</string>
<string name="enable_opengl_traces_title" msgid="4638773318659125196">"Monitorizări OpenGL"</string>
<string name="usb_audio_disable_routing" msgid="3367656923544254975">"Dezactivați rutarea audio USB"</string>
<string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Dezact. rutarea automată către perif. audio USB"</string>
<string name="debug_layout" msgid="1659216803043339741">"Afișează limite aspect"</string>
- <string name="debug_layout_summary" msgid="8825829038287321978">"Afișați limitele clipului, marginile etc."</string>
+ <string name="debug_layout_summary" msgid="8825829038287321978">"Afișează limitele clipului, marginile etc."</string>
<string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"Direcție aspect dreapta - stânga"</string>
<string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"Direcție obligatorie aspect ecran dreapta - stânga"</string>
<string name="window_blurs" msgid="6831008984828425106">"Permite estompări la nivel de fereastră"</string>
@@ -386,24 +386,24 @@
<string name="force_msaa_summary" msgid="9070437493586769500">"Activează MSAA 4x în aplicațiile OpenGL ES 2.0"</string>
<string name="show_non_rect_clip" msgid="7499758654867881817">"Remediezi decupări nerectangulare"</string>
<string name="track_frame_time" msgid="522674651937771106">"Profil redare cu HWUI"</string>
- <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Activați nivelurile de depanare GPU"</string>
- <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permiteți încărcarea nivelurilor de depanare GPU pentru aplicațiile de depanare"</string>
+ <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Activează nivelurile de depanare GPU"</string>
+ <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite încărcarea nivelurilor de depanare GPU pentru aplicațiile de depanare"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Activează înregistrarea detaliată a furnizorilor"</string>
- <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Includeți alte jurnale ale furnizorilor de dispozitive în rapoartele de eroare, care pot conține informații private, folosiți mai multă baterie și/sau mai mult spațiu de stocare."</string>
+ <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Include alte jurnale ale furnizorilor de dispozitive în rapoartele de eroare, care pot conține informații private, folosește mai multă baterie și/sau mai mult spațiu de stocare."</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"Scară animație fereastră"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Scară tranziție animații"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Scară durată Animator"</string>
<string name="overlay_display_devices_title" msgid="5411894622334469607">"Simulați afișaje secundare"</string>
<string name="debug_applications_category" msgid="5394089406638954196">"Aplicații"</string>
- <string name="immediately_destroy_activities" msgid="1826287490705167403">"Nu păstrați activitățile"</string>
+ <string name="immediately_destroy_activities" msgid="1826287490705167403">"Nu păstra activitățile"</string>
<string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Elimină activitățile imediat ce utilizatorul le închide"</string>
<string name="app_process_limit_title" msgid="8361367869453043007">"Limită procese fundal"</string>
- <string name="show_all_anrs" msgid="9160563836616468726">"Afișați ANR de fundal"</string>
+ <string name="show_all_anrs" msgid="9160563836616468726">"Afișează ANR de fundal"</string>
<string name="show_all_anrs_summary" msgid="8562788834431971392">"Afișează dialogul Aplicația nu răspunde pentru aplicațiile din fundal"</string>
<string name="show_notification_channel_warnings" msgid="3448282400127597331">"Afișează avertismentele de pe canalul de notificări"</string>
<string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"Afișează avertisment pe ecran când o aplicație postează o notificare fără canal valid"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"Forțează accesul aplicațiilor la stocarea externă"</string>
- <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permiteți scrierea oricărei aplicații eligibile în stocarea externă, indiferent de valorile manifestului"</string>
+ <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permite scrierea oricărei aplicații eligibile în stocarea externă, indiferent de valorile manifestului"</string>
<string name="force_resizable_activities" msgid="7143612144399959606">"Forțați redimensionarea activităților"</string>
<string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permiteți redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string>
<string name="enable_freeform_support" msgid="7599125687603914253">"Activează ferestrele cu formă liberă"</string>
@@ -428,13 +428,13 @@
</string-array>
<string name="inactive_apps_title" msgid="5372523625297212320">"Aplicații în standby"</string>
<string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactivă. Atinge pentru a comuta."</string>
- <string name="inactive_app_active_summary" msgid="8047630990208722344">"Activă. Atingeți pentru a comuta."</string>
+ <string name="inactive_app_active_summary" msgid="8047630990208722344">"Activă. Atinge pentru a comuta."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Stare Standby aplicații: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
<string name="transcode_settings_title" msgid="2581975870429850549">"Setări pentru transcodarea conținutului media"</string>
<string name="transcode_user_control" msgid="6176368544817731314">"Modifică setările prestabilite de transcodare"</string>
<string name="transcode_enable_all" msgid="2411165920039166710">"Activează transcodarea"</string>
<string name="transcode_default" msgid="3784803084573509491">"Presupune că aplicațiile acceptă formatele moderne"</string>
- <string name="transcode_notification" msgid="5560515979793436168">"Vedeți notificările privind transcodarea"</string>
+ <string name="transcode_notification" msgid="5560515979793436168">"Vezi notificările privind transcodarea"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"Dezactivează memoria cache pentru transcodare"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Servicii în curs de funcționare"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Vezi și controlează serviciile care funcționează în prezent"</string>
@@ -442,7 +442,7 @@
<string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"Setează implementarea WebView"</string>
<string name="select_webview_provider_toast_text" msgid="8512254949169359848">"Această opțiune nu mai este validă. Încearcă din nou."</string>
<string name="picture_color_mode" msgid="1013807330552931903">"Modul de culori pentru imagini"</string>
- <string name="picture_color_mode_desc" msgid="151780973768136200">"Folosiți sRGB"</string>
+ <string name="picture_color_mode_desc" msgid="151780973768136200">"Folosește sRGB"</string>
<string name="daltonizer_mode_disabled" msgid="403424372812399228">"Dezactivat"</string>
<string name="daltonizer_mode_monochromacy" msgid="362060873835885014">"Daltonism"</string>
<string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Deuteranomalie (roșu-verde)"</string>
@@ -515,7 +515,7 @@
<string name="active_input_method_subtypes" msgid="4232680535471633046">"Metode active de introducere de text"</string>
<string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"Folosește limbile sistemului"</string>
<string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"Deschiderea setărilor pentru <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> a eșuat"</string>
- <string name="ime_security_warning" msgid="6547562217880551450">"Această metodă de introducere de text poate culege în întregime textul introdus, inclusiv datele personale, cum ar fi parolele și numerele cardurilor de credit. Metoda provine de la aplicația <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Utilizați această metodă de introducere de text?"</string>
+ <string name="ime_security_warning" msgid="6547562217880551450">"Această metodă de introducere de text poate culege în întregime textul introdus, inclusiv datele personale, cum ar fi parolele și numerele cardurilor de credit. Metoda provine de la aplicația <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Folosești această metodă de introducere de text?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"Notă: după repornire, această aplicație nu poate porni până nu deblochezi telefonul"</string>
<string name="ims_reg_title" msgid="8197592958123671062">"Situația înregistrării IMS"</string>
<string name="ims_reg_status_registered" msgid="884916398194885457">"Înregistrat"</string>
@@ -538,13 +538,13 @@
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Niciodată"</string>
<string name="zen_interruption_level_priority" msgid="5392140786447823299">"Numai cu prioritate"</string>
<string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
- <string name="zen_alarm_warning_indef" msgid="4146527909616457163">"Dacă nu dezactivați această opțiune înainte, nu veți auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="zen_alarm_warning_indef" msgid="4146527909616457163">"Dacă nu dezactivezi această opțiune înainte, nu vei auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="zen_alarm_warning" msgid="245729928048586280">"Nu vei auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="3346777418136233330">"la <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="6382760514842998629">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Durată"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Întreabă de fiecare dată"</string>
- <string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivați"</string>
+ <string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivezi"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string>
<string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Acest telefon"</string>
<string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Această tabletă"</string>
@@ -554,7 +554,7 @@
<string name="help_label" msgid="3528360748637781274">"Ajutor și feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Stocare"</string>
<string name="shared_data_title" msgid="1017034836800864953">"Date la care se permite accesul"</string>
- <string name="shared_data_summary" msgid="5516326713822885652">"Vedeți și modificați datele la care se permite accesul"</string>
+ <string name="shared_data_summary" msgid="5516326713822885652">"Vezi și modifică datele la care se permite accesul"</string>
<string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Nu există date la care se permite accesul pentru acest utilizator."</string>
<string name="shared_data_query_failure_text" msgid="3489828881998773687">"A apărut o eroare la preluarea datelor la care se permite accesul. Încearcă din nou."</string>
<string name="blob_id_text" msgid="8680078988996308061">"ID-ul datelor la care se permite accesul: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
@@ -567,15 +567,15 @@
<string name="delete_blob_text" msgid="2819192607255625697">"Șterge datele la care se permite accesul"</string>
<string name="delete_blob_confirmation_text" msgid="7807446938920827280">"Sigur ștergi aceste date la care se permite accesul?"</string>
<string name="user_add_user_item_summary" msgid="5748424612724703400">"Utilizatorii dețin aplicații și materiale proprii"</string>
- <string name="user_add_profile_item_summary" msgid="5418602404308968028">"Puteți restricționa accesul la aplicații și la conținut din contul dvs."</string>
+ <string name="user_add_profile_item_summary" msgid="5418602404308968028">"Poți restricționa accesul la aplicații și la conținut din contul tău"</string>
<string name="user_add_user_item_title" msgid="2394272381086965029">"Utilizator"</string>
<string name="user_add_profile_item_title" msgid="3111051717414643029">"Profil limitat"</string>
- <string name="user_add_user_title" msgid="5457079143694924885">"Adăugați un utilizator nou?"</string>
- <string name="user_add_user_message_long" msgid="1527434966294733380">"Puteți să permiteți accesul la acest dispozitiv altor persoane creând utilizatori suplimentari. Fiecare utilizator are propriul spațiu, pe care îl poate personaliza cu aplicații, imagini de fundal etc. De asemenea, utilizatorii pot ajusta setările dispozitivului, cum ar fi setările pentru Wi-Fi, care îi afectează pe toți ceilalți utilizatori.\n\nDupă ce adăugați un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOricare dintre utilizatori poate actualiza aplicațiile pentru toți ceilalți utilizatori. Este posibil ca setările de accesibilitate și serviciile să nu se transfere la noul utilizator."</string>
- <string name="user_add_user_message_short" msgid="3295959985795716166">"Când adăugați un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOrice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string>
+ <string name="user_add_user_title" msgid="5457079143694924885">"Adaugi un utilizator nou?"</string>
+ <string name="user_add_user_message_long" msgid="1527434966294733380">"Poți să permiți accesul la acest dispozitiv altor persoane creând utilizatori suplimentari. Fiecare utilizator are propriul spațiu, pe care îl poate personaliza cu aplicații, imagini de fundal etc. De asemenea, utilizatorii pot ajusta setările dispozitivului, cum ar fi setările pentru Wi-Fi, care îi afectează pe toți ceilalți utilizatori.\n\nDupă ce adaugi un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOricare dintre utilizatori poate actualiza aplicațiile pentru toți ceilalți utilizatori. Este posibil ca setările de accesibilitate și serviciile să nu se transfere la noul utilizator."</string>
+ <string name="user_add_user_message_short" msgid="3295959985795716166">"Când adaugi un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOrice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurați utilizatorul acum?"</string>
- <string name="user_setup_dialog_message" msgid="269931619868102841">"Asigurați-vă că utilizatorul are posibilitatea de a prelua dispozitivul și de a-și configura spațiul"</string>
- <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Configurați profilul acum?"</string>
+ <string name="user_setup_dialog_message" msgid="269931619868102841">"Asigură-te că utilizatorul are posibilitatea de a prelua dispozitivul și de a-și configura spațiul"</string>
+ <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Configurezi profilul acum?"</string>
<string name="user_setup_button_setup_now" msgid="1708269547187760639">"Configurați acum"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"Nu acum"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"Adaugă"</string>
@@ -583,49 +583,49 @@
<string name="user_new_profile_name" msgid="2405500423304678841">"Profil nou"</string>
<string name="user_info_settings_title" msgid="6351390762733279907">"Info. utilizator"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"Informații de profil"</string>
- <string name="user_need_lock_message" msgid="4311424336209509301">"Înainte de a putea crea un profil cu permisiuni limitate, va trebui să configurați blocarea ecranului pentru a vă proteja aplicațiile și datele personale."</string>
- <string name="user_set_lock_button" msgid="1427128184982594856">"Configurați blocarea"</string>
+ <string name="user_need_lock_message" msgid="4311424336209509301">"Înainte de a putea crea un profil cu permisiuni limitate, va trebui să configurezi blocarea ecranului pentru a-ți proteja aplicațiile și datele personale."</string>
+ <string name="user_set_lock_button" msgid="1427128184982594856">"Configurează blocarea"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Treci la <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Se creează un utilizator nou…"</string>
<string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"Se creează un invitat nou…"</string>
<string name="add_user_failed" msgid="4809887794313944872">"Nu s-a creat noul utilizator"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Nu s-a putut crea un invitat nou"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
- <string name="user_add_user" msgid="7876449291500212468">"Adăugați un utilizator"</string>
+ <string name="user_add_user" msgid="7876449291500212468">"Adaugă un utilizator"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
<string name="guest_reset_guest" msgid="6110013010356013758">"Resetezi sesiunea pentru invitați"</string>
<string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Resetezi invitatul?"</string>
<string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Excludeți invitatul?"</string>
- <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetați"</string>
- <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eliminați"</string>
+ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetează"</string>
+ <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Elimină"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Se resetează invitatul…"</string>
- <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Resetați sesiunea pentru invitați?"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Resetezi sesiunea pentru invitați?"</string>
<string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Astfel, va începe o nouă sesiune pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea actuală"</string>
- <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ieșiți din modul pentru invitați?"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ieși din modul pentru invitați?"</string>
<string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală"</string>
<string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieșiți"</string>
<string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvezi activitatea invitatului?"</string>
<string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Salvează activitatea din sesiunea actuală sau șterge aplicațiile și datele"</string>
- <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ștergeți"</string>
- <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvați"</string>
- <string name="guest_exit_button" msgid="5774985819191803960">"Ieșiți din modul pentru invitați"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Șterge"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvează"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Ieși din modul pentru invitați"</string>
<string name="guest_reset_button" msgid="2515069346223503479">"Resetează sesiunea pentru invitați"</string>
- <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ieșiți din modul pentru invitați"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ieși din modul pentru invitați"</string>
<string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toate activitățile vor fi șterse la ieșire"</string>
<string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puteți să salvați sau să ștergeți activitatea la ieșire"</string>
- <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetați pentru a șterge acum activitatea din sesiune sau salvați ori ștergeți activitatea la ieșire"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetează pentru a șterge acum activitatea din sesiune sau salvează ori șterge activitatea la ieșire"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fă o fotografie"</string>
- <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Alege o imagine"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string>
<string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Prea multe încercări incorecte. Datele de pe acest dispozitiv vor fi șterse."</string>
<string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Prea multe încercări incorecte. Acest utilizator va fi șters."</string>
<string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Prea multe încercări incorecte. Acest profil de serviciu și datele sale vor fi șterse."</string>
- <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Respingeți"</string>
+ <string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Respinge"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Prestabilit pentru dispozitiv"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dezactivat"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pentru ca modificarea să se aplice, trebuie să reporniți dispozitivul. Reporniți-l acum sau anulați."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pentru ca modificarea să se aplice, trebuie să repornești dispozitivul. Repornește-l acum sau anulează."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Căști cu fir"</string>
<string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activat"</string>
<string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Dezactivat"</string>
@@ -656,7 +656,7 @@
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet deconectat."</string>
<string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
<string name="accessibility_no_calling" msgid="3540827068323895748">"Apelarea nu este disponibilă."</string>
- <string name="avatar_picker_title" msgid="8492884172713170652">"Alegeți o fotografie de profil"</string>
+ <string name="avatar_picker_title" msgid="8492884172713170652">"Alege o fotografie de profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Pictograma prestabilită a utilizatorului"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tastatură fizică"</string>
<string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"Alege aspectul tastaturii"</string>
@@ -669,6 +669,6 @@
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Difuzează <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
<string name="bt_le_audio_broadcast_dialog_different_output" msgid="2638402023060391333">"Schimbă rezultatul"</string>
<string name="back_navigation_animation" msgid="8105467568421689484">"Animații pentru gestul înapoi predictiv"</string>
- <string name="back_navigation_animation_summary" msgid="741292224121599456">"Activați animațiile de sistem pentru gestul înapoi predictiv."</string>
+ <string name="back_navigation_animation_summary" msgid="741292224121599456">"Activează animațiile de sistem pentru gestul înapoi predictiv."</string>
<string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Această setare activează animațiile de sistem pentru animația gesturilor predictive. Necesită setarea valorii true în cazul atributului enableOnBackInvokedCallback pentru fiecare aplicație în fișierul manifest."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 4387a41..8abc51a 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"සම්පූර්ණ වීමට <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතිරියි"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - සම්පූර්ණ වීමට <xliff:g id="TIME">%2$s</xliff:g>ක් ඉතිරියි"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය විරාම කර ඇත"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්ර ආරෝපණය"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"සෙමින් ආරෝපණය"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"නොරැහැන්ව ආරෝපණය වේ"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"ආරෝපණය වේ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ආරෝපණය නොවේ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"සම්බන්ධයි, ආරෝපණය නොවේ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"අරෝපිතයි"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index b116bd4..515f963 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> derisa të mbushet"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi është vendosur në pauzë"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Karikim i shpejtë"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Po karikohet ngadalë"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Po karikohet pa tel"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Po karikohet"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nuk po karikohet"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Lidhur, jo në karikim"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Karikuar"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 8457ef0..943ba5f6 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kvar tills fulladdat"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kvar tills fulladdat"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har pausats"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laddas långsamt"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laddas trådlöst"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Laddas"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ansluten, laddas inte"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Laddat"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 6ed8f42..b7124a63 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Zimesalia <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> zimesalia ijae chaji"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - Imesitisha kuchaji"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Inachaji pole pole"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Inachaji bila kutumia waya"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Inachaji"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Haichaji"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Imeunganishwa, haichaji"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Imechajiwa"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 118542f..de88c2c 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tamamen şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g>: Şarj işlemi duraklatıldı"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Yavaş şarj oluyor"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kablosuz şarj oluyor"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Şarj Etme"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Şarj olmuyor"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Bağlandı, şarj olmuyor"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Şarj oldu"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 817fa35..a438741 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -477,15 +477,13 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
- <!-- no translation found for power_charging_limited (6971664137170239141) -->
- <skip />
+ <string name="power_charging_limited" msgid="6971664137170239141">"<xliff:g id="LEVEL">%1$s</xliff:g> - Заряджання призупинено"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Повільне заряджання"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бездротове заряджання"</string>
- <!-- no translation found for battery_info_status_charging_dock (8573274094093364791) -->
- <skip />
+ <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Заряджання"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряджається"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Підключено, не заряджається"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Заряджено"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index b64dcca..91b852a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -81,12 +81,13 @@
device.onProfileStateChanged(A2dpProfile.this, BluetoothProfile.STATE_CONNECTED);
device.refresh();
}
- mIsProfileReady=true;
+ mIsProfileReady = true;
mProfileManager.callServiceConnectedListeners();
}
public void onServiceDisconnected(int profile) {
- mIsProfileReady=false;
+ mIsProfileReady = false;
+ mProfileManager.callServiceDisconnectedListeners();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index a491455..e22f3f0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -113,12 +113,13 @@
// Check current list of CachedDevices to see if any are Hearing Aid devices.
mDeviceManager.updateHearingAidsDevices();
- mIsProfileReady=true;
+ mIsProfileReady = true;
mProfileManager.callServiceConnectedListeners();
}
public void onServiceDisconnected(int profile) {
- mIsProfileReady=false;
+ mIsProfileReady = false;
+ mProfileManager.callServiceDisconnectedListeners();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
index 387bae1..5e66972 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
@@ -3,5 +3,7 @@
hughchen@google.com
timhypeng@google.com
robertluo@google.com
+changbetty@google.com
+songferngwang@google.com
# Emergency approvers in case the above are not available
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index b416738..39b4b8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -78,6 +78,9 @@
* Config the MobileStatusTracker to start or stop monitoring platform signals.
*/
public void setListening(boolean listening) {
+ if (mListening == listening) {
+ return;
+ }
mListening = listening;
if (listening) {
mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
index 63a9f0c..adfa39e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
@@ -21,8 +21,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -59,9 +57,9 @@
void startActivityForResult(Intent intent, int resultCode);
- boolean startSystemActivityForResult(Intent intent, int resultCode);
-
int getPhotoSize();
+
+ boolean canCropPhoto();
}
interface ContextInjector {
@@ -84,7 +82,6 @@
private static final long DELAY_BEFORE_CROP_MILLIS = 150;
private static final String IMAGES_DIR = "multi_user";
- private static final String PRE_CROP_PICTURE_FILE_NAME = "PreCropEditUserPhoto.jpg";
private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
@@ -94,7 +91,6 @@
private final ContextInjector mContextInjector;
private final File mImagesDir;
- private final Uri mPreCropPictureUri;
private final Uri mCropPictureUri;
private final Uri mTakePictureUri;
@@ -104,8 +100,6 @@
mImagesDir = new File(mContextInjector.getCacheDir(), IMAGES_DIR);
mImagesDir.mkdir();
- mPreCropPictureUri = mContextInjector
- .createTempImageUri(mImagesDir, PRE_CROP_PICTURE_FILE_NAME, !waiting);
mCropPictureUri =
mContextInjector.createTempImageUri(mImagesDir, CROP_PICTURE_FILE_NAME, !waiting);
mTakePictureUri =
@@ -137,7 +131,7 @@
return true;
case REQUEST_CODE_TAKE_PHOTO:
if (mTakePictureUri.equals(pictureUri)) {
- cropPhoto(pictureUri);
+ cropPhoto();
} else {
copyAndCropPhoto(pictureUri, false);
}
@@ -166,7 +160,7 @@
ThreadUtils.postOnBackgroundThread(() -> {
final ContentResolver cr = mContextInjector.getContentResolver();
try (InputStream in = cr.openInputStream(pictureUri);
- OutputStream out = cr.openOutputStream(mPreCropPictureUri)) {
+ OutputStream out = cr.openOutputStream(mTakePictureUri)) {
Streams.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to copy photo", e);
@@ -174,7 +168,7 @@
}
Runnable cropRunnable = () -> {
if (!mAvatarUi.isFinishing()) {
- cropPhoto(mPreCropPictureUri);
+ cropPhoto();
}
};
if (delayBeforeCrop) {
@@ -189,21 +183,22 @@
}
}
- private void cropPhoto(final Uri pictureUri) {
- // TODO: Use a public intent, when there is one.
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(pictureUri, "image/*");
- appendOutputExtra(intent, mCropPictureUri);
- appendCropExtras(intent);
- try {
- StrictMode.disableDeathOnFileUriExposure();
- if (mAvatarUi.startSystemActivityForResult(intent, REQUEST_CODE_CROP_PHOTO)) {
- return;
+ private void cropPhoto() {
+ if (mAvatarUi.canCropPhoto()) {
+ // TODO: Use a public intent, when there is one.
+ Intent intent = new Intent("com.android.camera.action.CROP");
+ intent.setDataAndType(mTakePictureUri, "image/*");
+ appendOutputExtra(intent, mCropPictureUri);
+ appendCropExtras(intent);
+ try {
+ StrictMode.disableDeathOnFileUriExposure();
+ mAvatarUi.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
+ } finally {
+ StrictMode.enableDeathOnFileUriExposure();
}
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
+ } else {
+ onPhotoNotCropped(mTakePictureUri);
}
- onPhotoNotCropped(pictureUri);
}
private void appendOutputExtra(Intent intent, Uri pictureUri) {
@@ -325,23 +320,15 @@
}
@Override
- public boolean startSystemActivityForResult(Intent intent, int code) {
- ActivityInfo info = intent.resolveActivityInfo(mActivity.getPackageManager(),
- PackageManager.MATCH_SYSTEM_ONLY);
- if (info == null) {
- Log.w(TAG, "No system package activity could be found for code " + code);
- return false;
- }
- intent.setPackage(info.packageName);
- mActivity.startActivityForResult(intent, code);
- return true;
- }
-
- @Override
public int getPhotoSize() {
return mActivity.getResources()
.getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
}
+
+ @Override
+ public boolean canCropPhoto() {
+ return PhotoCapabilityUtils.canCropPhoto(mActivity);
+ }
}
static class ContextInjectorImpl implements ContextInjector {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
index d988111..3dc2fab 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
@@ -34,7 +34,6 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
@@ -52,7 +51,6 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
@RunWith(AndroidJUnit4.class)
@@ -75,7 +73,6 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMockAvatarUi.getPhotoSize()).thenReturn(PHOTO_SIZE);
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(true);
mImagesDir = new File(
InstrumentationRegistry.getTargetContext().getCacheDir(), "multi_user");
@@ -113,7 +110,9 @@
}
@Test
- public void takePhotoIsFollowedByCrop() throws IOException {
+ public void takePhotoIsFollowedByCropWhenSupported() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -122,12 +121,14 @@
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void takePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -137,11 +138,12 @@
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
}
@Test
public void takePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
Intent intent = new Intent();
@@ -149,12 +151,14 @@
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void choosePhotoIsFollowedByCrop() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -163,12 +167,14 @@
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void choosePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -178,11 +184,12 @@
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
}
@Test
public void choosePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
Intent intent = new Intent();
@@ -190,11 +197,28 @@
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
+ public void choosePhotoIsNotFollowedByCropIntentWhenCropNotSupported() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(false);
+
+ File file = new File(mImagesDir, "file.txt");
+ saveBitmapToFile(file);
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
+
+ verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
+ }
+
+ @Test
public void cropPhotoResultIsReturnedIfResultOkAndContent() {
Intent intent = new Intent();
intent.setData(mCropPhotoUri);
@@ -218,58 +242,11 @@
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
}
- @Test
- public void cropDoesNotUseTakePhotoUri() throws IOException {
- new File(mImagesDir, "file.txt").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- Intent startIntent = verifyStartSystemActivityForResult(
- "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
- assertThat(startIntent.getData()).isNotEqualTo(mTakePhotoUri);
- }
-
- @Test
- public void internalCropUsedIfNoSystemCropperFound() throws IOException {
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(false);
-
- File file = new File(mImagesDir, "file.txt");
- saveBitmapToFile(file);
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
-
- InputStream imageStream = mContext.getContentResolver().openInputStream(mCropPhotoUri);
- Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
- assertThat(bitmap.getWidth()).isEqualTo(PHOTO_SIZE);
- assertThat(bitmap.getHeight()).isEqualTo(PHOTO_SIZE);
- }
-
- private Intent verifyStartActivityForResult(String action, int resultCode) {
+ private void verifyStartActivityForResult(String action, int resultCode) {
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
.startActivityForResult(captor.capture(), eq(resultCode));
- Intent intent = captor.getValue();
- assertThat(intent.getAction()).isEqualTo(action);
- return intent;
- }
-
- private Intent verifyStartSystemActivityForResult(String action, int resultCode) {
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
- .startSystemActivityForResult(captor.capture(), eq(resultCode));
- Intent intent = captor.getValue();
- assertThat(intent.getAction()).isEqualTo(action);
- return intent;
+ assertThat(captor.getValue().getAction()).isEqualTo(action);
}
private void saveBitmapToFile(File file) throws IOException {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index bb6b293..a252f52 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothA2dp;
@@ -78,6 +79,21 @@
.thenReturn(Arrays.asList(mDevice));
}
+
+ @Test
+ public void onServiceConnected_isProfileReady() {
+ assertThat(mProfile.isProfileReady()).isTrue();
+ verify(mProfileManager).callServiceConnectedListeners();
+ }
+
+ @Test
+ public void onServiceDisconnected_profileNotReady() {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
+
+ assertThat(mProfile.isProfileReady()).isFalse();
+ verify(mProfileManager).callServiceDisconnectedListeners();
+ }
+
@Test
public void supportsHighQualityAudio() {
when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidProfileTest.java
index be3a517..101a6cd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidProfileTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
@@ -66,7 +67,6 @@
mProfile = new HearingAidProfile(context, mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
- mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mService);
}
@Test
@@ -74,4 +74,20 @@
assertThat(mProfile.setActiveDevice(null)).isTrue();
assertThat(mProfile.setActiveDevice(mBluetoothDevice)).isTrue();
}
+
+ @Test
+ public void onServiceConnected_isProfileReady() {
+ mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, mService);
+
+ assertThat(mProfile.isProfileReady()).isTrue();
+ verify(mProfileManager).callServiceConnectedListeners();
+ }
+
+ @Test
+ public void onServiceDisconnected_profileNotReady() {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
+
+ assertThat(mProfile.isProfileReady()).isFalse();
+ verify(mProfileManager).callServiceDisconnectedListeners();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
new file mode 100644
index 0000000..fd181ff
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceViewHolder;
+
+
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppHeaderPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private AppHeaderPreference mPreference;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.app_header_preference, /* parent */ null);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPreference = new AppHeaderPreference(mContext);
+ }
+
+ @Test
+ public void setNonSelectable_viewShouldNotBeSelectable() {
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.itemView.isClickable()).isFalse();
+ }
+
+ @Test
+ public void defaultInstallType_viewShouldNotBeVisible() {
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.install_type).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setIsInstantApp_shouldUpdateInstallType() {
+
+ mPreference.onBindViewHolder(mHolder);
+ mPreference.setIsInstantApp(true);
+
+ assertThat(((TextView) mRootView.findViewById(R.id.install_type)).getText().toString())
+ .isEqualTo(mContext.getResources().getString(R.string.install_type_instant));
+ }
+
+ @Test
+ public void setSecondSummary_shouldUpdateSecondSummary() {
+ final String defaultTestText = "Test second summary";
+
+ mPreference.onBindViewHolder(mHolder);
+ mPreference.setSecondSummary(defaultTestText);
+
+ assertThat(((TextView) mRootView.findViewById(R.id.second_summary)).getText().toString())
+ .isEqualTo(defaultTestText);
+ }
+}
diff --git a/packages/SettingsProvider/res/values-ro/strings.xml b/packages/SettingsProvider/res/values-ro/strings.xml
index 561a213..db13019 100644
--- a/packages/SettingsProvider/res/values-ro/strings.xml
+++ b/packages/SettingsProvider/res/values-ro/strings.xml
@@ -21,5 +21,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Stocare setări"</string>
<string name="wifi_softap_config_change" msgid="5688373762357941645">"Setările hotspotului s-au modificat"</string>
- <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Atingeți pentru detalii"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Atinge pentru detalii"</string>
</resources>
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 1dfa96f..e7c7ac0 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -849,8 +849,6 @@
Settings.Secure.AMBIENT_CONTEXT_CONSENT_COMPONENT,
Settings.Secure.AMBIENT_CONTEXT_EVENT_ARRAY_EXTRA_KEY,
Settings.Secure.AMBIENT_CONTEXT_PACKAGE_NAME_EXTRA_KEY,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED,
Settings.Secure.AUTO_REVOKE_DISABLED,
Settings.Secure.BIOMETRIC_APP_ENABLED,
Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED,
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
index 625b15a..ce39385 100644
--- a/packages/Shell/res/values-nb/strings.xml
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -18,8 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Kommandoliste"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Feilrapporter"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Feilrapporten <xliff:g id="ID">#%d</xliff:g> blir generert"</string>
- <string name="bugreport_finished_title" msgid="4429132808670114081">"Feilrapporten <xliff:g id="ID">#%d</xliff:g> er fullført"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Feilrapport <xliff:g id="ID">#%d</xliff:g> blir generert"</string>
+ <string name="bugreport_finished_title" msgid="4429132808670114081">"Feilrapport <xliff:g id="ID">#%d</xliff:g> er fullført"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Legger til detaljer i feilrapporten"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Vent litt"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Feilrapporten vises snart på telefonen"</string>
@@ -38,7 +38,7 @@
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"Skjermdump"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Skjermdumpen er tatt."</string>
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Skjermdumpen kunne ikke tas."</string>
- <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Detaljer om feilrapporten <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Detaljer om feilrapport <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"Filnavn"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"Navn på feil"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"Oppsummering av feil"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ba8079f..b7fb472 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -213,6 +213,7 @@
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
"kotlinx_coroutines_test",
+ "kotlin-reflect",
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
@@ -244,11 +245,9 @@
},
}
-// Opt-in config for optimizing the SystemUI target using R8.
-// Enabled via `export SYSTEMUI_OPTIMIZE_JAVA=true`, or explicitly in Make via
-// the `SOONG_CONFIG_ANDROID_SYSTEMUI_OPTIMIZE_JAVA` variable.
-// TODO(b/203472868): Enable optimizations by default after stabilizing and
-// building out retrace infrastructure.
+// Opt-out config for optimizing the SystemUI target using R8.
+// Disabled via `export SYSTEMUI_OPTIMIZE_JAVA=false`, or explicitly in Make via
+// `SYSTEMUI_OPTIMIZE_JAVA := false`.
soong_config_module_type {
name: "systemui_optimized_java_defaults",
module_type: "java_defaults",
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
index 11477f9..6588e22 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
@@ -83,7 +83,11 @@
flowOf(
securityText?.let { text ->
SecurityButtonConfig(
- icon = Icon.Resource(R.drawable.ic_info_outline),
+ icon =
+ Icon.Resource(
+ R.drawable.ic_info_outline,
+ contentDescription = null,
+ ),
text = text,
isClickable = securityClickable,
)
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 3ca8dfe..7275712 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -39,6 +39,7 @@
-packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
-packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
-packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt
+-packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
-packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
-packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
-packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
@@ -156,6 +157,8 @@
-packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
-packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
-packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+-packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt
+-packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
-packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
-packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
-packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -246,6 +249,7 @@
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipRootView.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -332,7 +336,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarWifiView.kt
-packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
--packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
-packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt
-packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -356,6 +359,7 @@
-packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiStatusTrackerFactory.kt
-packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
-packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -575,6 +579,7 @@
-packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
-packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -633,6 +638,7 @@
-packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
-packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
-packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
-packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
-packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
-packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
@@ -720,6 +726,7 @@
-packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
-packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
-packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -744,7 +751,6 @@
-packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
-packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -757,6 +763,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 1ac1e3a..01e5d86 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -17,7 +17,6 @@
import android.graphics.drawable.Drawable
import android.view.View
import com.android.systemui.plugins.annotations.ProvidesInterface
-import com.android.systemui.shared.regionsampling.RegionDarkness
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
@@ -62,7 +61,7 @@
/** Initializes various rendering parameters. If never called, provides reasonable defaults. */
fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
- events.onColorPaletteChanged(resources, RegionDarkness.DEFAULT, RegionDarkness.DEFAULT)
+ events.onColorPaletteChanged(resources, true, true)
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
@@ -92,8 +91,8 @@
/** Call whenever the color palette should update */
fun onColorPaletteChanged(
resources: Resources,
- smallClockIsDark: RegionDarkness,
- largeClockIsDark: RegionDarkness
+ smallClockIsDark: Boolean,
+ largeClockIsDark: Boolean
) { }
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 8b8ebf0..3ad7c8c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -23,6 +23,7 @@
android:id="@+id/keyguard_clock_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clipChildren="false"
android:layout_gravity="center_horizontal|top">
<FrameLayout
android:id="@+id/lockscreen_clock_view"
@@ -30,16 +31,13 @@
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
- android:paddingStart="@dimen/clock_padding_start">
- </FrameLayout>
+ android:paddingStart="@dimen/clock_padding_start" />
<FrameLayout
android:id="@+id/lockscreen_clock_view_large"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_below="@id/keyguard_slice_view"
- android:paddingTop="@dimen/keyguard_large_clock_top_padding"
- android:visibility="gone">
- </FrameLayout>
+ android:layout_marginTop="@dimen/keyguard_large_clock_top_margin"
+ android:visibility="gone" />
<!-- Not quite optimal but needed to translate these items as a group. The
NotificationIconContainer has its own logic for translation. -->
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 712f657..5936ead 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -17,23 +17,24 @@
*/
-->
-<com.android.keyguard.KeyguardPINView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/keyguard_pin_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_gravity="center_horizontal|bottom"
- android:orientation="vertical"
- >
+<com.android.keyguard.KeyguardPINView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width">
- <androidx.constraintlayout.widget.ConstraintLayout
+<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pin_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_weight="1"
android:layoutDirection="ltr"
android:orientation="vertical">
@@ -79,6 +80,8 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter"
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 86546ed..be52c44 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"ልክ ያልሆነ ካርድ።"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"ባትሪ ሞልቷል"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በገመድ አልባ ኃይል በመሙላት ላይ"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ኃይል በመሙላት ላይ"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ኃይል በመሙላት ላይ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ባትሪን ለመጠበቅ ኃይል መሙላት ባለበት ቆሟል"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ለመክፈት ምናሌ ተጫን።"</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"አውታረ መረብ ተቆልፏል"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"ምንም ሲም ካርድ የለም"</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 06da1bf..b22655a 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"ব্যৱহাৰৰ অযোগ্য ছিম কাৰ্ড"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"চ্চার্জ কৰা হ’ল"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • বেতাঁৰৰ জৰিয়তে চাৰ্জ কৰি থকা হৈছে"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চাৰ্জ কৰি থকা হৈছে"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চ্চার্জ কৰি থকা হৈছে"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্ৰুত গতিৰে চ্চাৰ্জ কৰি থকা হৈছে"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • লাহে লাহে চ্চাৰ্জ কৰি থকা হৈছে"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • বেটাৰী সুৰক্ষিত কৰিবলৈ চাৰ্জিং পজ কৰা হৈছে"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক কৰিবলৈ মেনু টিপক।"</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটৱর্ক লক কৰা অৱস্থাত আছে"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"কোনো ছিম কাৰ্ড নাই"</string>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index d508d09..616d31a 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Несапраўдная картка."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Зараджаны"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе бесправадная зарадка"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе зарадка"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе зарадка"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе хуткая зарадка"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе павольная зарадка"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарадка прыпынена дзеля ашчады акумулятара"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Націсніце кнопку \"Меню\", каб разблакіраваць."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Сетка заблакіравана"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Няма SIM-карты"</string>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index 68db723..331c355 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Ugyldigt kort."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Opladet"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Trådløs opladning"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader hurtigt"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader langsomt"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladningen er sat på pause for at beskytte batteriet"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tryk på menuen for at låse op."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Netværket er låst"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Intet SIM-kort"</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 05c5e92..c19b357 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Ungültige Karte."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Aufgeladen"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kabelloses Laden"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird schnell geladen"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladevorgang angehalten, um den Akku zu schonen"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Zum Entsperren die Menütaste drücken."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Netzwerk gesperrt"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Keine SIM-Karte"</string>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index 94437b3..dceb78e 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Kehtetu kaart."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Laetud"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Juhtmeta laadimine"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kiirlaadimine"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aeglane laadimine"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine on peatatud, et akut kaitsta"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Vajutage avamiseks menüüklahvi."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Võrk on lukus"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"SIM-kaarti pole"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index c63cb1a..f8cec42 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Virheellinen kortti"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Ladattu"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan langattomasti"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan nopeasti"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan hitaasti"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lataus on keskeytetty akun suojaamiseksi"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Poista lukitus painamalla Valikkoa."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Verkko lukittu"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Ei SIM-korttia"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index bab0af6..01facd1 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Carte non valide."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Chargé"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • En charge sans fil"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge…"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge rapide…"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge lente…"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • La recharge est en pause pour protéger la batterie"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur \"Menu\" pour déverrouiller le clavier."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Pas de carte SIM"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index a579acb..4fbdd67 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"A tarxeta non é válida."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Cargado"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando sen fíos"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rapidamente"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carga púxose en pausa para protexer a batería"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Preme Menú para desbloquear."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada pola rede"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Sen tarxeta SIM"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index c3ba5b7..3412026 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Սխալ քարտ"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Լիցքավորված է"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Անլար լիցքավորում"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Արագ լիցքավորում"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Դանդաղ լիցքավորում"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորումը դադարեցվել է՝ մարտկոցը պաշտպանելու համար"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ապակողպելու համար սեղմեք Ընտրացանկը:"</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Ցանցը կողպված է"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"SIM քարտ չկա"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 4b27503..6abdc82 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Ógilt kort."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Fullhlaðin"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í þráðlausri hleðslu"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hröð hleðsla"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Gert var hlé á hleðslu til að vernda rafhlöðuna"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ýttu á valmyndarhnappinn til að taka úr lás."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Net læst"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Ekkert SIM-kort"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 8c81ebe..d6d5bcd 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Жарамсыз карта."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Зарядталды"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Сымсыз зарядталуда"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталып жатыр."</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталуда"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жылдам зарядталуда"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Баяу зарядталуда"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батареяны қорғау мақсатында зарядтау кідіртілді."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ашу үшін \"Мәзір\" пернесін басыңыз."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Желі құлыптаулы"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"SIM картасы салынбаған"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 34b2181..00bfe05 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"បណ្ណមិនត្រឹមត្រូវទេ។"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"បានសាកថ្មពេញ"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្មឥតខ្សែ"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្ម"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្ម"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្មយឺត"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ការសាកថ្មត្រូវបានផ្អាក ដើម្បីការពារថ្ម"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ចុចម៉ឺនុយ ដើម្បីដោះសោ។"</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"បណ្ដាញជាប់សោ"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"គ្មានស៊ីមកាតទេ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index 4d1f9a0..d371a4b 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Nederīga karte."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Akumulators uzlādēts"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek bezvadu uzlāde"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek uzlāde"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek uzlāde"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek ātrā uzlāde"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek lēnā uzlāde"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Uzlāde ir pārtraukta, lai aizsargātu akumulatoru"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lai atbloķētu, nospiediet izvēlnes ikonu."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Tīkls ir bloķēts."</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Nav SIM kartes."</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index 45a4dfb1..ef22564 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Неважечка картичка."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Полна"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни безжично"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо полнење"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бавно полнење"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Полнењето е паузирано за да се заштити батеријата"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притиснете „Мени“ за отклучување."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заклучена"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Нема SIM-картичка"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index dcaee63..409f727 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"ਅਵੈਧ ਕਾਰਡ।"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"ਚਾਰਜ ਹੋ ਗਿਆ"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ਅਣਲਾਕ ਕਰਨ ਲਈ \"ਮੀਨੂ\" ਦਬਾਓ।"</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"ਨੈੱਟਵਰਕ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"ਕੋਈ ਸਿਮ ਕਾਰਡ ਨਹੀਂ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index aa659a8..52bc982 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Nieprawidłowa karta."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Naładowana"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie bezprzewodowe"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Szybkie ładowanie"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne ładowanie"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wstrzymano ładowanie, aby chronić baterię"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Naciśnij Menu, aby odblokować."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieć zablokowana"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Brak karty SIM"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index d83328b..2b8f8d6 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Ошибка SIM-карты."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Батарея заряжена"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Беспроводная зарядка"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядка"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"Идет зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"Идет быстрая зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"Идет медленная зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Чтобы продлить срок службы батареи, зарядка приостановлена"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Для разблокировки нажмите \"Меню\"."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Сеть заблокирована"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Нет SIM-карты."</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 580acef..4e911de 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"වලංගු නොවන කාඩ්පත."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"අරෝපිතයි"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • නොරැහැන්ව ආරෝපණ කෙරේ"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින්"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරෝපණය වෙමින්"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • බැටරිය ආරක්ෂා කිරීම සඳහා ආරෝපණය විරාම කර ඇත"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"අගුලු හැරීමට මෙනුව ඔබන්න."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"ජාලය අගුළු දමා ඇත"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"SIM පත නැත"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 4001bff..772308f 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Neveljavna kartica"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Baterija napolnjena"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • brezžično polnjenje"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Polnjenje"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • polnjenje"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • hitro polnjenje"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • počasno polnjenje"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Zaradi zaščite baterije je polnjenje začasno zaustavljeno"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Če želite odkleniti, pritisnite meni."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Omrežje je zaklenjeno"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Ni kartice SIM"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index b42def6..c758462 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Karta e pavlefshme."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"I karikuar"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet me valë"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet me shpejtësi"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet ngadalë"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Karikimi është vendosur në pauzë për të mbrojtur baterinë"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Shtyp \"Meny\" për të shkyçur."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Rrjeti është i kyçur"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Nuk ka kartë SIM"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index 40abf95..fa241d9 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Ogiltigt kort."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Laddat"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas trådlöst"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas snabbt"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas långsamt"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddningen har pausats för att skydda batteriet"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lås upp genom att trycka på Meny."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Nätverk låst"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Inget SIM-kort"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index 816b6aa..791bceb 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Kadi si Sahihi."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Betri imejaa"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji bila kutumia waya"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji kwa kasi"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji pole pole"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Imesitisha kuchaji ili kulinda betri"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Bonyeza Menyu ili kufungua."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Mtandao umefungwa"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Hakuna SIM kadi"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index 4e144a0..54aaae3 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Geçersiz Kart."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Şarj oldu"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kablosuz olarak şarj ediliyor"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj oluyor"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj oluyor"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hızlı şarj oluyor"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş şarj oluyor"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj işlemi pili korumak için duraklatıldı"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmak için Menü\'ye basın."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Ağ kilitli"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"SIM kart yok"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index d9730fac..6144c1c 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Недійсна картка."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Заряджено"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бездротове заряджання"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Швидке заряджання"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне заряджання"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання призупинено, щоб захистити акумулятор"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натисніть меню, щоб розблокувати."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Мережу заблоковано"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Немає SIM-карти"</string>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 66badc8..1d6cfa8 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -26,13 +26,11 @@
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Thẻ không hợp lệ."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Đã sạc đầy"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc không dây"</string>
- <!-- no translation found for keyguard_plugged_in_dock (2122073051904360987) -->
- <skip />
+ <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc nhanh"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc chậm"</string>
- <!-- no translation found for keyguard_plugged_in_charging_limited (1709413803451065875) -->
- <skip />
+ <string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đã tạm dừng sạc để bảo vệ pin"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Nhấn vào Menu để mở khóa."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Mạng đã bị khóa"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Không có thẻ SIM"</string>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index deab610..04dffb6 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -50,7 +50,7 @@
<item name="android:background">@null</item>
<item name="android:textSize">32sp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:paddingBottom">-16dp</item>
</style>
<style name="Widget.TextView.Password" parent="@android:style/Widget.TextView">
diff --git a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
index 9b43cf6..779ab81 100644
--- a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
+++ b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:fillColor="@color/media_dialog_inactive_item_main_content"
+ android:fillColor="@color/media_dialog_item_main_content"
android:pathData="M12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,12zM12,20q3.325,0 5.663,-2.337Q20,15.325 20,12t-2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,3.325 2.338,5.663Q8.675,20 12,20z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_present_to_all.xml b/packages/SystemUI/res/drawable/ic_present_to_all.xml
new file mode 100644
index 0000000..d6c9bbe
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_present_to_all.xml
@@ -0,0 +1,25 @@
+ <!--
+ ~ 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.
+ -->
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M21,3L3,3c-1.11,0 -2,0.89 -2,2v14c0,1.11 0.89,2 2,2h18c1.11,0 2,-0.89 2,-2L23,5c0,-1.11 -0.89,-2 -2,-2zM21,19.02L3,19.02L3,4.98h18v14.04zM8,12l4,-4 4,4 -1.41,1.41L13,11.83L13,16h-2v-4.17l-1.59,1.59L8,12z"/>
+ </vector>
diff --git a/packages/SystemUI/res/drawable/media_output_status_failed.xml b/packages/SystemUI/res/drawable/media_output_status_failed.xml
index 05c6358..0599e23 100644
--- a/packages/SystemUI/res/drawable/media_output_status_failed.xml
+++ b/packages/SystemUI/res/drawable/media_output_status_failed.xml
@@ -21,6 +21,6 @@
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
- android:fillColor="@color/media_dialog_inactive_item_main_content"
+ android:fillColor="@color/media_dialog_item_main_content"
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
</vector>
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index d633803..be76405 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -51,7 +51,7 @@
android:id="@+id/biometric_icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal">
+ android:layout_gravity="center">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/biometric_icon"
@@ -61,6 +61,13 @@
android:contentDescription="@null"
android:scaleType="fitXY" />
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/biometric_icon_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY" />
</FrameLayout>
<!-- For sensors such as UDFPS, this view is used during custom measurement/layout to add extra
diff --git a/packages/SystemUI/res/layout/media_projection_app_selector.xml b/packages/SystemUI/res/layout/media_projection_app_selector.xml
new file mode 100644
index 0000000..4ad6849
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_projection_app_selector.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.internal.widget.ResolverDrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ androidprv:maxCollapsedHeight="0dp"
+ androidprv:maxCollapsedHeightSmall="56dp"
+ androidprv:maxWidth="@*android:dimen/chooser_width"
+ android:id="@*android:id/contentPanel">
+
+ <LinearLayout
+ android:id="@*android:id/chooser_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ androidprv:layout_alwaysShow="true"
+ android:gravity="center"
+ android:elevation="0dp"
+ android:background="@*android:drawable/bottomsheet_background">
+
+ <ImageView
+ android:id="@*android:id/icon"
+ android:layout_width="@dimen/media_projection_app_selector_icon_size"
+ android:layout_height="@dimen/media_projection_app_selector_icon_size"
+ android:layout_marginTop="@*android:dimen/chooser_edge_margin_normal"
+ android:layout_marginBottom="@*android:dimen/chooser_edge_margin_normal"
+ android:importantForAccessibility="no"
+ android:tint="?android:attr/textColorPrimary"/>
+
+ <TextView android:id="@*android:id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:gravity="center"
+ android:paddingBottom="@*android:dimen/chooser_view_spacing"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@*android:id/content_preview_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <TabHost
+ android:id="@*android:id/profile_tabhost"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:background="?android:attr/colorBackground">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TabWidget
+ android:id="@*android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ </TabWidget>
+ <FrameLayout
+ android:id="@*android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.internal.app.ResolverViewPager
+ android:id="@*android:id/profile_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
+
+</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Fingerprint_To_Error_Landscape.json b/packages/SystemUI/res/raw/BiometricPrompt_Fingerprint_To_Error_Landscape.json
new file mode 100644
index 0000000..3d33b2a
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Fingerprint_To_Error_Landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Fingerprint_To_Error_Landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Landscape_Base.json b/packages/SystemUI/res/raw/BiometricPrompt_Landscape_Base.json
new file mode 100644
index 0000000..7310a04
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Landscape_Base.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Landscape_Base","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null_Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle mask 3","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-60,"s":[55]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":110,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":140,"s":[10]},{"t":170,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-60,"s":[92.146,-65.896,0],"to":[1.361,6.667,0],"ti":[-1.361,-6.667,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.167,"y":0.167},"t":0,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":110,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[100.313,-25.896,0]}],"ix":2,"l":2},"a":{"a":0,"k":[160.315,58.684,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.013,2.518],[5.251,5.023],[8.982,-2.829],[-0.264,-5.587]],"o":[[12.768,-2.854],[-14.961,2.071],[-6.004,1.89],[8.052,1.403]],"v":[[5.115,7.499],[19.814,-10.087],[-16.489,-3.588],[-24.801,8.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[34.67,28.053],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.231,-7],[-27.395,-1.197],[-26.792,4.092],[14.179,15.736]],"o":[[-17.931,5.646],[56.062,2.45],[-1.765,-22.396],[-51.819,17.744]],"v":[[-62.102,-8.314],[-39.958,30.079],[80.033,25.905],[54.879,-32.529]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[80.283,32.779],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"circle mask 7","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"circle mask","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"circle mask 6","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circle mask 2","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"circle mask 4","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":1,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circle mask 5","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":".black","cl":"black","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[0,18.167,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[0,10.667,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 4","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"device frame mask","parent":24,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,1.167,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".blue400","cl":"blue400","parent":18,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-115.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-199,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-159,"s":[100.25,-105.667,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-0.75,-14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"device frame mask 5","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-165,"op":6.00000000000001,"st":-271,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"device frame mask 9","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-145,"s":[50]},{"t":-75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-165,"s":[0,0]},{"t":-75,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":73,"s":[50]},{"t":113,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"device frame mask 8","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-165,"s":[50]},{"t":-95,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-195,"s":[0,0]},{"t":-105,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[50]},{"t":83,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"device frame mask 7","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-195,"s":[50]},{"t":-125,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-225,"s":[0,0]},{"t":-135,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":53,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"device frame mask 6","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-225,"s":[50]},{"t":-155,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-255,"s":[0,0]},{"t":-165,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[50]},{"t":23,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Portrait_Base_BottomRight.json b/packages/SystemUI/res/raw/BiometricPrompt_Portrait_Base_BottomRight.json
new file mode 100644
index 0000000..4e29490f
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Portrait_Base_BottomRight.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Portrait_Base_BottomRight","ddd":0,"assets":[{"id":"comp_0","nm":"BiometricPrompt_Landscape_Base","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null_Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle mask 3","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-60,"s":[55]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":110,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":140,"s":[10]},{"t":170,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-60,"s":[92.146,-65.896,0],"to":[1.361,6.667,0],"ti":[-1.361,-6.667,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.167,"y":0.167},"t":0,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":110,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[100.313,-25.896,0]}],"ix":2,"l":2},"a":{"a":0,"k":[160.315,58.684,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.013,2.518],[5.251,5.023],[8.982,-2.829],[-0.264,-5.587]],"o":[[12.768,-2.854],[-14.961,2.071],[-6.004,1.89],[8.052,1.403]],"v":[[5.115,7.499],[19.814,-10.087],[-16.489,-3.588],[-24.801,8.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[34.67,28.053],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.231,-7],[-27.395,-1.197],[-26.792,4.092],[14.179,15.736]],"o":[[-17.931,5.646],[56.062,2.45],[-1.765,-22.396],[-51.819,17.744]],"v":[[-62.102,-8.314],[-39.958,30.079],[80.033,25.905],[54.879,-32.529]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[80.283,32.779],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"circle mask 7","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"circle mask","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"circle mask 6","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circle mask 2","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"circle mask 4","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":1,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circle mask 5","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":".black","cl":"black","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[0,18.167,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[0,10.667,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 4","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"device frame mask","parent":24,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,1.167,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".blue400","cl":"blue400","parent":18,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-115.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-199,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-159,"s":[100.25,-105.667,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-0.75,-14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"device frame mask 5","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-165,"op":6.00000000000001,"st":-271,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"device frame mask 9","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-145,"s":[50]},{"t":-75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-165,"s":[0,0]},{"t":-75,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":73,"s":[50]},{"t":113,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"device frame mask 8","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-165,"s":[50]},{"t":-95,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-195,"s":[0,0]},{"t":-105,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[50]},{"t":83,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"device frame mask 7","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-195,"s":[50]},{"t":-125,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-225,"s":[0,0]},{"t":-135,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":53,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"device frame mask 6","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-225,"s":[50]},{"t":-155,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-255,"s":[0,0]},{"t":-165,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[50]},{"t":23,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0}]}],"layers":[{"ddd":0,"ind":6,"ty":0,"nm":"BiometricPrompt_Landscape_Base","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[170,170,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":340,"h":340,"ip":0,"op":900,"st":0,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Portrait_Base_TopLeft.json b/packages/SystemUI/res/raw/BiometricPrompt_Portrait_Base_TopLeft.json
new file mode 100644
index 0000000..ff6f12f
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Portrait_Base_TopLeft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Portrait_Base_TopLeft","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":6,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null_Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey905","cl":"grey905","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"circle mask 3","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Finger_Flipped","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-24.98,-35.709,0],"ix":2,"l":2},"a":{"a":0,"k":[31.791,75.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.03,5.25],[-2.83,8.98],[-5.59,-0.26],[2.52,-11.02]],"o":[[-2.85,12.77],[2.07,-14.96],[1.9,-6],[1.4,8.05],[0,0]],"v":[[7.5,4.99],[-10.09,19.69],[-3.59,-16.61],[8.69,-24.92],[7.5,5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.8,24.94],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.01,22.23],[-1.2,-27.39],[4.09,-26.79],[15.73,14.18]],"o":[[5.64,-17.93],[2.45,56.06],[-22.4,-1.77],[17.73,-51.82]],"v":[[-7.57,-66.9],[30.82,-44.76],[26.65,75.23],[-31.78,50.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[31.79,75.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"circle mask 7","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"circle mask","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey904","cl":"grey904","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"circle mask 6","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey903","cl":"grey903","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"circle mask 2","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".blue400","cl":"blue400","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"circle mask 4","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":1,"nm":".grey902","cl":"grey902","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"circle mask 5","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":1,"nm":".black","cl":"black","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".grey800","cl":"grey800","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".grey901","cl":"grey901","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-87.156,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[100.25,-94.656,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 4","parent":6,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".grey900","cl":"grey900","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Landscape.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Landscape.json
new file mode 100644
index 0000000..ba8a63c
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Error_To_Fingerprint_Landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_BottomRight.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_BottomRight.json
new file mode 100644
index 0000000..0653a11
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_BottomRight.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_BottomRight","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 12","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_TopLeft.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_TopLeft.json
new file mode 100644
index 0000000..97de8b7
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_TopLeft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_TopLeft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 12","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Landscape.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Landscape.json
new file mode 100644
index 0000000..af4ce2e
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Error_To_Success_Landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Portrait_BottomRight.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Portrait_BottomRight.json
new file mode 100644
index 0000000..7a38840
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Portrait_BottomRight.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Error_To_Success_Portrait_BottomRight","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 13","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Portrait_TopLeft.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Portrait_TopLeft.json
new file mode 100644
index 0000000..e1612d0f
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Error_To_Success_Portrait_TopLeft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Error_To_Success_Portrait_TopLeft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 13","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_BottomRight.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_BottomRight.json
new file mode 100644
index 0000000..c27f7a3
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_BottomRight.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_BottomRight","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 14","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_TopLeft.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_TopLeft.json
new file mode 100644
index 0000000..aca0655
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_TopLeft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_TopLeft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 14","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Landscape.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Landscape.json
new file mode 100644
index 0000000..aa46dfb
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Fingerprint_To_Success_Landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_BottomRight.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_BottomRight.json
new file mode 100644
index 0000000..5cf7cac
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_BottomRight.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_BottomRight","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 15","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_TopLeft.json b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_TopLeft.json
new file mode 100644
index 0000000..317ae9d
--- /dev/null
+++ b/packages/SystemUI/res/raw/BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_TopLeft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_TopLeft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 15","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 54c5490..dc2bee5 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -66,10 +66,12 @@
<!-- media output dialog-->
<color name="media_dialog_background">@color/material_dynamic_neutral10</color>
- <color name="media_dialog_active_item_main_content">@color/material_dynamic_neutral10</color>
- <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_neutral10</color>
- <color name="media_dialog_item_status">@color/material_dynamic_neutral10</color>
- <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
+ <color name="media_dialog_item_main_content">@color/material_dynamic_primary90</color>
+ <color name="media_dialog_item_background">@color/material_dynamic_neutral_variant20</color>
+ <color name="media_dialog_connected_item_background">@color/material_dynamic_secondary20</color>
+ <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color>
+ <color name="media_dialog_button_background">@color/material_dynamic_primary70</color>
+ <color name="media_dialog_solid_button_text">@color/material_dynamic_secondary20</color>
<!-- Biometric dialog colors -->
<color name="biometric_dialog_gray">#ffcccccc</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ec22c60..ae9ebba 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -617,8 +617,9 @@
<!-- Which face help messages to surface when fingerprint is also enrolled.
Message ids correspond with the acquired ids in BiometricFaceConstants -->
<integer-array name="config_face_help_msgs_when_fingerprint_enrolled">
- <item>25</item>
- <item>26</item>
+ <item>3</item> <!-- TOO_DARK -->
+ <item>25</item> <!-- DARK_GLASSES -->
+ <item>26</item> <!-- MOUTH_COVERING_DETECTED -->
</integer-array>
<!-- Whether the communal service should be enabled -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a19145d..19a6aba 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -669,7 +669,7 @@
<!-- When large clock is showing, offset the smartspace by this amount -->
<dimen name="keyguard_smartspace_top_offset">12dp</dimen>
<!-- With the large clock, move up slightly from the center -->
- <dimen name="keyguard_large_clock_top_padding">100dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
<dimen name="notification_scrim_corner_radius">32dp</dimen>
@@ -1453,6 +1453,8 @@
<dimen name="fgs_manager_list_top_spacing">12dp</dimen>
+ <dimen name="media_projection_app_selector_icon_size">32dp</dimen>
+
<!-- Dream overlay related dimensions -->
<dimen name="dream_overlay_status_bar_height">60dp</dimen>
<dimen name="dream_overlay_status_bar_margin">40dp</dimen>
@@ -1475,14 +1477,14 @@
if their end is aligned with the parent end. Represented as the percentage over from the
start of the parent container. -->
<item name="dream_overlay_complication_guide_end_percent" format="float" type="dimen">
- 0.75
+ 0.5
</item>
<!-- The position of the start guide, which dream overlay complications can align their end to
if their start is aligned with the parent start. Represented as the percentage over from
the start of the parent container. -->
<item name="dream_overlay_complication_guide_start_percent" format="float" type="dimen">
- 0.25
+ 0.5
</item>
<!-- The position of the bottom guide, which dream overlay complications can align their top to
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index f88f46f..8084254 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -183,5 +183,7 @@
<!-- face scanning view id -->
<item type="id" name="face_scanning_anim"/>
+
+ <item type="id" name="qqs_tile_layout"/>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d5a744b..9324e8f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1644,8 +1644,9 @@
<!-- The tile in quick settings is unavailable. [CHAR LIMIT=32] -->
<string name="tile_unavailable">Unavailable</string>
- <!-- The tile in quick settings is disabled by a device administration policy [CHAR LIMIT=32] -->
- <string name="tile_disabled">Disabled</string>
+ <!-- Accessibility text for the click action on a tile that is disabled by policy. This will
+ be used following "Double-tap to..." [CHAR LIMIT=NONE] -->
+ <string name="accessibility_tile_disabled_by_policy_action_description">learn more</string>
<!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] -->
<string name="nav_bar">Navigation bar</string>
@@ -1861,10 +1862,10 @@
<!-- URL for care instructions for overheating devices -->
<string name="high_temp_dialog_help_url" translatable="false"></string>
- <!-- Title for alarm dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=30] -->
- <string name="high_temp_alarm_title">Unplug charger</string>
- <!-- Text body for dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=300] -->
- <string name="high_temp_alarm_notify_message">There\u2019s an issue charging this device. Unplug the power adapter, and take care as the cable may be warm.</string>
+ <!-- Title for alarm dialog alerting user the usb charger or accessorry has reached a certain temperature that should unplug the device immediately. [CHAR LIMIT=30] -->
+ <string name="high_temp_alarm_title">Unplug your device</string>
+ <!-- Text body for dialog alerting user the usb charger or accessorry has reached a certain temperature that should unplug the device immediately. [CHAR LIMIT=300] -->
+ <string name="high_temp_alarm_notify_message">Your device is getting warm near the charging port. If it\u2019s connected to a charger or USB accessory, unplug it, and take care as the cable may also be warm.</string>
<!-- Text for See care steps button [CHAR LIMIT=300] -->
<string name="high_temp_alarm_help_care_steps">See care steps</string>
<!-- Text link directs to usb overheat help page. -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6d1bcbb..8af432a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -477,7 +477,7 @@
<style name="MediaOutputItemInactiveTitle">
<item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/media_dialog_inactive_item_main_content</item>
+ <item name="android:textColor">@color/media_dialog_item_main_content</item>
</style>
<style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
@@ -978,6 +978,8 @@
<!-- Padding for indeterminate progress bar -->
<item name="progressBarStartPadding">12dp</item>
<item name="progressBarEndPadding">16dp</item>
+
+ <item name="iconSize">25dp</item>
</style>
<style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
index 0fac76d..f3866c0 100644
--- a/packages/SystemUI/res/xml/combined_qs_header_scene.xml
+++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
@@ -29,17 +29,15 @@
app:percentX="0"
app:percentY="0"
app:framePosition="49"
- app:percentWidth="1"
- app:percentHeight="1"
+ app:sizePercent="0"
app:curveFit="linear"
app:motionTarget="@id/date" />
<KeyPosition
app:keyPositionType="deltaRelative"
app:percentX="1"
app:percentY="0.51"
+ app:sizePercent="1"
app:framePosition="51"
- app:percentWidth="1"
- app:percentHeight="1"
app:curveFit="linear"
app:motionTarget="@id/date" />
<KeyAttribute
@@ -64,6 +62,7 @@
app:percentX="0"
app:percentY="0"
app:framePosition="50"
+ app:sizePercent="0"
app:curveFit="linear"
app:motionTarget="@id/statusIcons" />
<KeyPosition
@@ -71,6 +70,7 @@
app:percentX="1"
app:percentY="0.51"
app:framePosition="51"
+ app:sizePercent="1"
app:curveFit="linear"
app:motionTarget="@id/statusIcons" />
<KeyAttribute
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt
new file mode 100644
index 0000000..2e391c7
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.testing.screenshot
+
+import android.app.Activity
+import android.graphics.Color
+import android.view.View
+import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.WindowInsetsControllerCompat
+import androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import platform.test.screenshot.*
+
+/**
+ * A rule that allows to run a screenshot diff test on a view that is hosted in another activity.
+ */
+class ExternalViewScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule {
+
+ private val colorsRule = MaterialYouColorsRule()
+ private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
+ private val screenshotRule =
+ ScreenshotTestRule(
+ SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec))
+ )
+ private val delegateRule =
+ RuleChain.outerRule(colorsRule).around(deviceEmulationRule).around(screenshotRule)
+ private val matcher = UnitTestBitmapMatcher
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return delegateRule.apply(base, description)
+ }
+
+ /**
+ * Compare the content of the [view] with the golden image identified by [goldenIdentifier] in
+ * the context of [emulationSpec].
+ */
+ fun screenshotTest(goldenIdentifier: String, view: View) {
+ view.removeElevationRecursively()
+
+ ScreenshotRuleAsserter.Builder(screenshotRule)
+ .setScreenshotProvider { view.toBitmap() }
+ .withMatcher(matcher)
+ .build()
+ .assertGoldenImage(goldenIdentifier)
+ }
+
+ /**
+ * Compare the content of the [activity] with the golden image identified by [goldenIdentifier]
+ * in the context of [emulationSpec].
+ */
+ fun activityScreenshotTest(
+ goldenIdentifier: String,
+ activity: Activity,
+ ) {
+ val rootView = activity.window.decorView
+
+ // Hide system bars, remove insets, focus and make sure device-specific cutouts
+ // don't affect screenshots
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val window = activity.window
+ window.setDecorFitsSystemWindows(false)
+ WindowInsetsControllerCompat(window, rootView).apply {
+ hide(WindowInsetsCompat.Type.systemBars())
+ systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ }
+
+ window.statusBarColor = Color.TRANSPARENT
+ window.navigationBarColor = Color.TRANSPARENT
+ window.attributes =
+ window.attributes.apply {
+ layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+ }
+
+ rootView.removeInsetsRecursively()
+ activity.currentFocus?.clearFocus()
+ }
+
+ screenshotTest(goldenIdentifier, rootView)
+ }
+}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/TestAppComponentFactory.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/TestAppComponentFactory.kt
new file mode 100644
index 0000000..98e9aaf
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/TestAppComponentFactory.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.testing.screenshot
+
+import android.app.Activity
+import android.content.Intent
+import androidx.core.app.AppComponentFactory
+
+class TestAppComponentFactory : AppComponentFactory() {
+
+ init {
+ instance = this
+ }
+
+ private val overrides: MutableMap<String, () -> Activity> = hashMapOf()
+
+ fun clearOverrides() {
+ overrides.clear()
+ }
+
+ fun <T : Activity> registerActivityOverride(activity: Class<T>, provider: () -> T) {
+ overrides[activity.name] = provider
+ }
+
+ override fun instantiateActivityCompat(
+ cl: ClassLoader,
+ className: String,
+ intent: Intent?
+ ): Activity {
+ return overrides
+ .getOrDefault(className) { super.instantiateActivityCompat(cl, className, intent) }
+ .invoke()
+ }
+
+ companion object {
+
+ private var instance: TestAppComponentFactory? = null
+
+ fun getInstance(): TestAppComponentFactory =
+ instance
+ ?: error(
+ "TestAppComponentFactory is not initialized, " +
+ "did you specify it in the manifest?"
+ )
+ }
+}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt
new file mode 100644
index 0000000..b84d26a
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.testing.screenshot
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.util.children
+import android.view.WindowInsets
+
+/**
+ * Elevation/shadows is not deterministic when doing hardware rendering, this exentsion allows to
+ * disable it for any view in the hierarchy.
+ */
+fun View.removeElevationRecursively() {
+ this.elevation = 0f
+ (this as? ViewGroup)?.children?.forEach(View::removeElevationRecursively)
+}
+
+/**
+ * Different devices could have different insets (e.g. different height of the navigation bar or
+ * taskbar). This method dispatches empty insets to the whole view hierarchy and removes
+ * the original listener, so the views won't receive real insets.
+ */
+fun View.removeInsetsRecursively() {
+ this.dispatchApplyWindowInsets(WindowInsets.CONSUMED)
+ this.setOnApplyWindowInsetsListener(null)
+ (this as? ViewGroup)?.children?.forEach(View::removeInsetsRecursively)
+}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
index c609e6f..cdedc64 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
@@ -1,10 +1,12 @@
package com.android.systemui.testing.screenshot
+import android.annotation.WorkerThread
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.graphics.Bitmap
import android.graphics.Canvas
+import android.graphics.HardwareRenderer
import android.graphics.Rect
import android.os.Build
import android.os.Handler
@@ -19,8 +21,13 @@
import androidx.concurrent.futures.ResolvableFuture
import androidx.test.annotation.ExperimentalTestApi
import androidx.test.core.internal.os.HandlerExecutor
+import androidx.test.espresso.Espresso
import androidx.test.platform.graphics.HardwareRendererCompat
+import com.google.common.util.concurrent.FutureCallback
+import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
+import kotlin.coroutines.suspendCoroutine
+import kotlinx.coroutines.runBlocking
/*
* This file was forked from androidx/test/core/view/ViewCapture.kt to add [Window] parameter to
@@ -62,6 +69,47 @@
}
/**
+ * Synchronously captures an image of the view into a [Bitmap]. Synchronous equivalent of
+ * [captureToBitmap].
+ */
+@WorkerThread
+@ExperimentalTestApi
+@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
+fun View.toBitmap(window: Window? = null): Bitmap {
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ error("toBitmap() can't be called from the main thread")
+ }
+
+ if (!HardwareRenderer.isDrawingEnabled()) {
+ error("Hardware rendering is not enabled")
+ }
+
+ // Make sure we are idle.
+ Espresso.onIdle()
+
+ val mainExecutor = context.mainExecutor
+ return runBlocking {
+ suspendCoroutine { continuation ->
+ Futures.addCallback(
+ captureToBitmap(window),
+ object : FutureCallback<Bitmap> {
+ override fun onSuccess(result: Bitmap) {
+ continuation.resumeWith(Result.success(result))
+ }
+
+ override fun onFailure(t: Throwable) {
+ continuation.resumeWith(Result.failure(t))
+ }
+ },
+ // We know that we are not on the main thread, so we can block the current
+ // thread and wait for the result in the main thread.
+ mainExecutor,
+ )
+ }
+ }
+}
+
+/**
* Trigger a redraw of the given view.
*
* Should only be called on UI thread.
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
index 60130e1..0b0595f 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
@@ -19,21 +19,13 @@
import android.app.Activity
import android.app.Dialog
import android.graphics.Bitmap
-import android.graphics.HardwareRenderer
-import android.os.Looper
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.view.Window
import androidx.activity.ComponentActivity
-import androidx.test.espresso.Espresso
import androidx.test.ext.junit.rules.ActivityScenarioRule
-import com.google.common.util.concurrent.FutureCallback
-import com.google.common.util.concurrent.Futures
-import kotlin.coroutines.suspendCoroutine
-import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
@@ -44,9 +36,13 @@
import platform.test.screenshot.MaterialYouColorsRule
import platform.test.screenshot.ScreenshotTestRule
import platform.test.screenshot.getEmulatedDevicePathConfig
+import platform.test.screenshot.matchers.BitmapMatcher
/** A rule for View screenshot diff unit tests. */
-class ViewScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule {
+class ViewScreenshotTestRule(
+ emulationSpec: DeviceEmulationSpec,
+ private val matcher: BitmapMatcher = UnitTestBitmapMatcher
+) : TestRule {
private val colorsRule = MaterialYouColorsRule()
private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
private val screenshotRule =
@@ -59,7 +55,6 @@
.around(deviceEmulationRule)
.around(screenshotRule)
.around(activityRule)
- private val matcher = UnitTestBitmapMatcher
override fun apply(base: Statement, description: Description): Statement {
return delegateRule.apply(base, description)
@@ -86,6 +81,8 @@
// Elevation/shadows is not deterministic when doing hardware rendering, so we disable
// it for any view in the hierarchy.
window.decorView.removeElevationRecursively()
+
+ activity.currentFocus?.clearFocus()
}
// We call onActivity again because it will make sure that our Activity is done measuring,
@@ -147,53 +144,11 @@
}
}
- private fun View.removeElevationRecursively() {
- this.elevation = 0f
-
- if (this is ViewGroup) {
- repeat(childCount) { i -> getChildAt(i).removeElevationRecursively() }
- }
- }
-
private fun Dialog.toBitmap(): Bitmap {
val window = window
return window.decorView.toBitmap(window)
}
- private fun View.toBitmap(window: Window? = null): Bitmap {
- if (Looper.getMainLooper() == Looper.myLooper()) {
- error("toBitmap() can't be called from the main thread")
- }
-
- if (!HardwareRenderer.isDrawingEnabled()) {
- error("Hardware rendering is not enabled")
- }
-
- // Make sure we are idle.
- Espresso.onIdle()
-
- val mainExecutor = context.mainExecutor
- return runBlocking {
- suspendCoroutine { continuation ->
- Futures.addCallback(
- captureToBitmap(window),
- object : FutureCallback<Bitmap> {
- override fun onSuccess(result: Bitmap?) {
- continuation.resumeWith(Result.success(result!!))
- }
-
- override fun onFailure(t: Throwable) {
- continuation.resumeWith(Result.failure(t))
- }
- },
- // We know that we are not on the main thread, so we can block the current
- // thread and wait for the result in the main thread.
- mainExecutor,
- )
- }
- }
- }
-
enum class Mode(val layoutParams: LayoutParams) {
WrapContent(LayoutParams(WRAP_CONTENT, WRAP_CONTENT)),
MatchSize(LayoutParams(MATCH_PARENT, MATCH_PARENT)),
diff --git a/packages/SystemUI/shared/res/layout/clock_default_small.xml b/packages/SystemUI/shared/res/layout/clock_default_small.xml
index 390ff5e..ff6d7f9 100644
--- a/packages/SystemUI/shared/res/layout/clock_default_small.xml
+++ b/packages/SystemUI/shared/res/layout/clock_default_small.xml
@@ -18,7 +18,7 @@
-->
<com.android.systemui.shared.clocks.AnimatableClockView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:gravity="start"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index a21a78b..34e2e83 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -29,6 +29,7 @@
import android.widget.TextView
import com.android.internal.R.attr.contentDescription
import com.android.internal.R.attr.format
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.TextAnimator
@@ -77,6 +78,9 @@
private var textAnimator: TextAnimator? = null
private var onTextAnimatorInitialized: Runnable? = null
+ @VisibleForTesting var isAnimationEnabled: Boolean = true
+ @VisibleForTesting var timeOverrideInMillis: Long? = null
+
val dozingWeight: Int
get() = if (useBoldedVersion()) dozingWeightInternal + 100 else dozingWeightInternal
@@ -139,7 +143,7 @@
}
fun refreshTime() {
- time.timeInMillis = System.currentTimeMillis()
+ time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis()
contentDescription = DateFormat.format(descFormat, time)
val formattedText = DateFormat.format(format, time)
// Setting text actually triggers a layout pass (because the text view is set to
@@ -215,7 +219,7 @@
}
fun animateAppearOnLockscreen() {
- if (textAnimator == null) {
+ if (isAnimationEnabled && textAnimator == null) {
return
}
setTextStyle(
@@ -231,7 +235,7 @@
weight = lockScreenWeight,
textSize = -1f,
color = lockScreenColor,
- animate = true,
+ animate = isAnimationEnabled,
duration = APPEAR_ANIM_DURATION,
delay = 0,
onAnimationEnd = null
@@ -239,7 +243,7 @@
}
fun animateFoldAppear(animate: Boolean = true) {
- if (textAnimator == null) {
+ if (isAnimationEnabled && textAnimator == null) {
return
}
setTextStyle(
@@ -255,7 +259,7 @@
weight = dozingWeightInternal,
textSize = -1f,
color = dozingColor,
- animate = animate,
+ animate = animate && isAnimationEnabled,
interpolator = Interpolators.EMPHASIZED_DECELERATE,
duration = ANIMATION_DURATION_FOLD_TO_AOD.toLong(),
delay = 0,
@@ -273,7 +277,7 @@
weight = if (isDozing()) dozingWeight else lockScreenWeight,
textSize = -1f,
color = null,
- animate = true,
+ animate = isAnimationEnabled,
duration = CHARGE_ANIM_DURATION_PHASE_1,
delay = 0,
onAnimationEnd = null
@@ -283,7 +287,7 @@
weight = if (isDozing()) lockScreenWeight else dozingWeight,
textSize = -1f,
color = null,
- animate = true,
+ animate = isAnimationEnabled,
duration = CHARGE_ANIM_DURATION_PHASE_0,
delay = chargeAnimationDelay.toLong(),
onAnimationEnd = startAnimPhase2
@@ -295,7 +299,7 @@
weight = if (isDozing) dozingWeight else lockScreenWeight,
textSize = -1f,
color = if (isDozing) dozingColor else lockScreenColor,
- animate = animate,
+ animate = animate && isAnimationEnabled,
duration = DOZE_ANIM_DURATION,
delay = 0,
onAnimationEnd = null
@@ -329,7 +333,7 @@
weight = weight,
textSize = textSize,
color = color,
- animate = animate,
+ animate = animate && isAnimationEnabled,
duration = duration,
interpolator = interpolator,
delay = delay,
@@ -367,7 +371,7 @@
weight = weight,
textSize = textSize,
color = color,
- animate = animate,
+ animate = animate && isAnimationEnabled,
interpolator = null,
duration = duration,
delay = delay,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 6c49186..19ac2e4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -13,12 +13,15 @@
*/
package com.android.systemui.shared.clocks
+import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
+import android.widget.FrameLayout
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.Clock
import com.android.systemui.plugins.ClockAnimations
@@ -27,7 +30,6 @@
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.shared.R
-import com.android.systemui.shared.regionsampling.RegionDarkness
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
@@ -39,6 +41,7 @@
/** Provides the default system clock */
class DefaultClockProvider @Inject constructor(
+ val ctx: Context,
val layoutInflater: LayoutInflater,
@Main val resources: Resources
) : ClockProvider {
@@ -49,7 +52,7 @@
if (id != DEFAULT_CLOCK_ID) {
throw IllegalArgumentException("$id is unsupported by $TAG")
}
- return DefaultClock(layoutInflater, resources)
+ return DefaultClock(ctx, layoutInflater, resources)
}
override fun getClockThumbnail(id: ClockId): Drawable? {
@@ -69,14 +72,13 @@
* AnimatableClockView used by the existing lockscreen clock.
*/
class DefaultClock(
+ ctx: Context,
private val layoutInflater: LayoutInflater,
private val resources: Resources
) : Clock {
- override val smallClock =
- layoutInflater.inflate(R.layout.clock_default_small, null) as AnimatableClockView
- override val largeClock =
- layoutInflater.inflate(R.layout.clock_default_large, null) as AnimatableClockView
- private val clocks = listOf(smallClock, largeClock)
+ override val smallClock: AnimatableClockView
+ override val largeClock: AnimatableClockView
+ private val clocks get() = listOf(smallClock, largeClock)
private val burmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"))
private val burmeseNumerals = burmeseNf.format(FORMAT_NUMBER.toLong())
@@ -84,20 +86,46 @@
resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
- private var smallRegionDarkness = RegionDarkness.DEFAULT
- private var largeRegionDarkness = RegionDarkness.DEFAULT
+ override val events: ClockEvents
+ override lateinit var animations: ClockAnimations
+ private set
- private fun updateClockColor(clock: AnimatableClockView, isRegionDark: RegionDarkness) {
- val color = if (isRegionDark.isDark) {
- resources.getColor(android.R.color.system_accent2_100)
- } else {
- resources.getColor(android.R.color.system_accent1_600)
- }
- clock.setColors(DOZE_COLOR, color)
- clock.animateAppearOnLockscreen()
+ private var smallRegionDarkness = false
+ private var largeRegionDarkness = false
+
+ init {
+ val parent = FrameLayout(ctx)
+
+ smallClock = layoutInflater.inflate(
+ R.layout.clock_default_small,
+ parent,
+ false
+ ) as AnimatableClockView
+
+ largeClock = layoutInflater.inflate(
+ R.layout.clock_default_large,
+ parent,
+ false
+ ) as AnimatableClockView
+
+ events = DefaultClockEvents()
+ animations = DefaultClockAnimations(0f, 0f)
+
+ events.onLocaleChanged(Locale.getDefault())
+
+ // DOZE_COLOR is a placeholder, and will be assigned correctly in initialize
+ clocks.forEach { it.setColors(DOZE_COLOR, DOZE_COLOR) }
}
- override val events = object : ClockEvents {
+ override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
+ recomputePadding()
+ animations = DefaultClockAnimations(dozeFraction, foldFraction)
+ events.onColorPaletteChanged(resources, true, true)
+ events.onTimeZoneChanged(TimeZone.getDefault())
+ events.onTimeTick()
+ }
+
+ inner class DefaultClockEvents() : ClockEvents {
override fun onTimeTick() = clocks.forEach { it.refreshTime() }
override fun onTimeFormatChanged(is24Hr: Boolean) =
@@ -120,8 +148,8 @@
override fun onColorPaletteChanged(
resources: Resources,
- smallClockIsDark: RegionDarkness,
- largeClockIsDark: RegionDarkness
+ smallClockIsDark: Boolean,
+ largeClockIsDark: Boolean
) {
if (smallRegionDarkness != smallClockIsDark) {
smallRegionDarkness = smallClockIsDark
@@ -145,9 +173,6 @@
}
}
- override var animations = DefaultClockAnimations(0f, 0f)
- private set
-
inner class DefaultClockAnimations(
dozeFraction: Float,
foldFraction: Float
@@ -203,35 +228,26 @@
}
}
- init {
- events.onLocaleChanged(Locale.getDefault())
- clocks.forEach { it.setColors(DOZE_COLOR, DOZE_COLOR) }
- }
-
- override fun initialize(
- resources: Resources,
- dozeFraction: Float,
- foldFraction: Float
- ) {
- recomputePadding()
- animations = DefaultClockAnimations(dozeFraction, foldFraction)
- events.onColorPaletteChanged(
- resources,
- RegionDarkness.DEFAULT,
- RegionDarkness.DEFAULT
- )
- events.onTimeTick()
+ private fun updateClockColor(clock: AnimatableClockView, isRegionDark: Boolean) {
+ val color = if (isRegionDark) {
+ resources.getColor(android.R.color.system_accent1_100)
+ } else {
+ resources.getColor(android.R.color.system_accent2_600)
+ }
+ clock.setColors(DOZE_COLOR, color)
+ clock.animateAppearOnLockscreen()
}
private fun recomputePadding() {
- val topPadding = -1 * (largeClock.bottom.toInt() - 180)
- largeClock.setPadding(0, topPadding, 0, 0)
+ val lp = largeClock.getLayoutParams() as FrameLayout.LayoutParams
+ lp.topMargin = (-0.5f * largeClock.bottom).toInt()
+ largeClock.setLayoutParams(lp)
}
override fun dump(pw: PrintWriter) = clocks.forEach { it.dump(pw) }
companion object {
- private const val DOZE_COLOR = Color.WHITE
+ @VisibleForTesting const val DOZE_COLOR = Color.WHITE
private const val FORMAT_NUMBER = 1234567890
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt
similarity index 100%
rename from packages/SystemUI/plugin/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt
rename to packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 0d1158c..0e1e0cb 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -21,6 +21,8 @@
import android.content.IntentFilter
import android.content.res.Resources
import android.text.format.DateFormat
+import android.util.TypedValue
+import android.view.View
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -41,7 +43,7 @@
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
* [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
*/
-class ClockEventController @Inject constructor(
+open class ClockEventController @Inject constructor(
private val statusBarStateController: StatusBarStateController,
private val broadcastDispatcher: BroadcastDispatcher,
private val batteryController: BatteryController,
@@ -74,18 +76,21 @@
private val updateFun = object : RegionSamplingInstance.UpdateColorCallback {
override fun updateColors() {
- smallClockIsDark = smallRegionSamplingInstance.currentRegionDarkness()
- largeClockIsDark = largeRegionSamplingInstance.currentRegionDarkness()
-
+ if (regionSamplingEnabled) {
+ smallClockIsDark = smallRegionSamplingInstance.currentRegionDarkness().isDark
+ largeClockIsDark = largeRegionSamplingInstance.currentRegionDarkness().isDark
+ } else {
+ val isLightTheme = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
+ smallClockIsDark = isLightTheme.data == 0
+ largeClockIsDark = isLightTheme.data == 0
+ }
clock?.events?.onColorPaletteChanged(resources, smallClockIsDark, largeClockIsDark)
}
}
fun updateRegionSamplers(currentClock: Clock?) {
- smallRegionSamplingInstance.stopRegionSampler()
- largeRegionSamplingInstance.stopRegionSampler()
-
- smallRegionSamplingInstance = RegionSamplingInstance(
+ smallRegionSamplingInstance = createRegionSampler(
currentClock?.smallClock,
mainExecutor,
bgExecutor,
@@ -93,7 +98,7 @@
updateFun
)
- largeRegionSamplingInstance = RegionSamplingInstance(
+ largeRegionSamplingInstance = createRegionSampler(
currentClock?.largeClock,
mainExecutor,
bgExecutor,
@@ -107,24 +112,26 @@
updateFun.updateColors()
}
- var smallRegionSamplingInstance: RegionSamplingInstance = RegionSamplingInstance(
- clock?.smallClock,
+ protected open fun createRegionSampler(
+ sampledView: View?,
+ mainExecutor: Executor?,
+ bgExecutor: Executor?,
+ regionSamplingEnabled: Boolean,
+ updateFun: RegionSamplingInstance.UpdateColorCallback
+ ): RegionSamplingInstance {
+ return RegionSamplingInstance(
+ sampledView,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
- updateFun
- )
+ updateFun)
+ }
- var largeRegionSamplingInstance: RegionSamplingInstance = RegionSamplingInstance(
- clock?.largeClock,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- updateFun
- )
+ lateinit var smallRegionSamplingInstance: RegionSamplingInstance
+ lateinit var largeRegionSamplingInstance: RegionSamplingInstance
- private var smallClockIsDark = smallRegionSamplingInstance.currentRegionDarkness()
- private var largeClockIsDark = largeRegionSamplingInstance.currentRegionDarkness()
+ private var smallClockIsDark = true
+ private var largeClockIsDark = true
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
@@ -179,7 +186,6 @@
init {
isDozing = statusBarStateController.isDozing
- clock?.events?.onColorPaletteChanged(resources, smallClockIsDark, largeClockIsDark)
}
fun registerListeners() {
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
new file mode 100644
index 0000000..6fcb6f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
@@ -0,0 +1,255 @@
+/*
+ * 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.keyguard
+
+import android.annotation.StringDef
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED
+import com.android.keyguard.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED
+import com.android.keyguard.FaceAuthApiRequestReason.Companion.QS_EXPANDED
+import com.android.keyguard.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER
+import com.android.keyguard.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN
+import com.android.keyguard.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED
+import com.android.keyguard.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.keyguard.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
+import com.android.keyguard.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
+import com.android.keyguard.InternalFaceAuthReasons.BIOMETRIC_ENABLED
+import com.android.keyguard.InternalFaceAuthReasons.CAMERA_LAUNCHED
+import com.android.keyguard.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
+import com.android.keyguard.InternalFaceAuthReasons.DREAM_STARTED
+import com.android.keyguard.InternalFaceAuthReasons.DREAM_STOPPED
+import com.android.keyguard.InternalFaceAuthReasons.ENROLLMENTS_CHANGED
+import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTHENTICATED
+import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT
+import com.android.keyguard.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED
+import com.android.keyguard.InternalFaceAuthReasons.FACE_LOCKOUT_RESET
+import com.android.keyguard.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP
+import com.android.keyguard.InternalFaceAuthReasons.FP_AUTHENTICATED
+import com.android.keyguard.InternalFaceAuthReasons.FP_LOCKED_OUT
+import com.android.keyguard.InternalFaceAuthReasons.GOING_TO_SLEEP
+import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY
+import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_INIT
+import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
+import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET
+import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
+import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
+import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
+import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
+import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
+import com.android.keyguard.InternalFaceAuthReasons.STARTED_WAKING_UP
+import com.android.keyguard.InternalFaceAuthReasons.TRUST_DISABLED
+import com.android.keyguard.InternalFaceAuthReasons.TRUST_ENABLED
+import com.android.keyguard.InternalFaceAuthReasons.USER_SWITCHING
+
+/**
+ * List of reasons why face auth is requested by clients through
+ * [KeyguardUpdateMonitor.requestFaceAuth].
+ */
+@Retention(AnnotationRetention.SOURCE)
+@StringDef(
+ SWIPE_UP_ON_BOUNCER,
+ UDFPS_POINTER_DOWN,
+ NOTIFICATION_PANEL_CLICKED,
+ QS_EXPANDED,
+ PICK_UP_GESTURE_TRIGGERED,
+)
+annotation class FaceAuthApiRequestReason {
+ companion object {
+ const val SWIPE_UP_ON_BOUNCER = "Face auth due to swipe up on bouncer"
+ const val UDFPS_POINTER_DOWN = "Face auth triggered due to finger down on UDFPS"
+ const val NOTIFICATION_PANEL_CLICKED = "Face auth due to notification panel click."
+ const val QS_EXPANDED = "Face auth due to QS expansion."
+ const val PICK_UP_GESTURE_TRIGGERED =
+ "Face auth due to pickup gesture triggered when the device is awake and not from AOD."
+ }
+}
+
+/** List of events why face auth could be triggered by [KeyguardUpdateMonitor]. */
+private object InternalFaceAuthReasons {
+ const val OCCLUDING_APP_REQUESTED = "Face auth due to request from occluding app."
+ const val RETRY_AFTER_HW_UNAVAILABLE = "Face auth due to retry after hardware unavailable."
+ const val FACE_LOCKOUT_RESET = "Face auth due to face lockout reset."
+ const val DEVICE_WOKEN_UP_ON_REACH_GESTURE =
+ "Face auth requested when user reaches for the device on AoD."
+ const val ALTERNATE_BIOMETRIC_BOUNCER_SHOWN = "Face auth due to alternate bouncer shown."
+ const val PRIMARY_BOUNCER_SHOWN = "Face auth started/stopped due to primary bouncer shown."
+ const val PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN =
+ "Face auth started/stopped due to bouncer being shown or will be shown."
+ const val TRUST_DISABLED = "Face auth started due to trust disabled."
+ const val TRUST_ENABLED = "Face auth stopped due to trust enabled."
+ const val KEYGUARD_OCCLUSION_CHANGED =
+ "Face auth started/stopped due to keyguard occlusion change."
+ const val ASSISTANT_VISIBILITY_CHANGED =
+ "Face auth started/stopped due to assistant visibility change."
+ const val STARTED_WAKING_UP = "Face auth started/stopped due to device starting to wake up."
+ const val DREAM_STOPPED = "Face auth due to dream stopped."
+ const val ALL_AUTHENTICATORS_REGISTERED = "Face auth due to all authenticators registered."
+ const val ENROLLMENTS_CHANGED = "Face auth due to enrolments changed."
+ const val KEYGUARD_VISIBILITY_CHANGED =
+ "Face auth stopped or started due to keyguard visibility changed."
+ const val FACE_CANCEL_NOT_RECEIVED = "Face auth stopped due to face cancel signal not received."
+ const val AUTH_REQUEST_DURING_CANCELLATION =
+ "Another request to start face auth received while cancelling face auth"
+ const val DREAM_STARTED = "Face auth stopped because dreaming started"
+ const val FP_LOCKED_OUT = "Face auth stopped because fp locked out"
+ const val FACE_AUTH_STOPPED_ON_USER_INPUT =
+ "Face auth stopped because user started typing password/pin"
+ const val KEYGUARD_GOING_AWAY = "Face auth stopped because keyguard going away"
+ const val CAMERA_LAUNCHED = "Face auth started/stopped because camera launched"
+ const val FP_AUTHENTICATED = "Face auth started/stopped because fingerprint launched"
+ const val GOING_TO_SLEEP = "Face auth started/stopped because going to sleep"
+ const val FINISHED_GOING_TO_SLEEP = "Face auth stopped because finished going to sleep"
+ const val KEYGUARD_INIT = "Face auth started/stopped because Keyguard is initialized"
+ const val KEYGUARD_RESET = "Face auth started/stopped because Keyguard is reset"
+ const val USER_SWITCHING = "Face auth started/stopped because user is switching"
+ const val FACE_AUTHENTICATED = "Face auth started/stopped because face is authenticated"
+ const val BIOMETRIC_ENABLED =
+ "Face auth started/stopped because biometric is enabled on keyguard"
+}
+
+/** UiEvents that are logged to identify why face auth is being triggered. */
+enum class FaceAuthUiEvent constructor(private val id: Int, val reason: String) :
+ UiEventLogger.UiEventEnum {
+ @UiEvent(doc = OCCLUDING_APP_REQUESTED)
+ FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED(1146, OCCLUDING_APP_REQUESTED),
+
+ @UiEvent(doc = UDFPS_POINTER_DOWN)
+ FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN(1147, UDFPS_POINTER_DOWN),
+
+ @UiEvent(doc = SWIPE_UP_ON_BOUNCER)
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER(1148, SWIPE_UP_ON_BOUNCER),
+
+ @UiEvent(doc = DEVICE_WOKEN_UP_ON_REACH_GESTURE)
+ FACE_AUTH_TRIGGERED_ON_REACH_GESTURE_ON_AOD(1149, DEVICE_WOKEN_UP_ON_REACH_GESTURE),
+
+ @UiEvent(doc = FACE_LOCKOUT_RESET)
+ FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET(1150, FACE_LOCKOUT_RESET),
+
+ @UiEvent(doc = QS_EXPANDED)
+ FACE_AUTH_TRIGGERED_QS_EXPANDED(1151, QS_EXPANDED),
+
+ @UiEvent(doc = NOTIFICATION_PANEL_CLICKED)
+ FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED(1152, NOTIFICATION_PANEL_CLICKED),
+
+ @UiEvent(doc = PICK_UP_GESTURE_TRIGGERED)
+ FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED(1153, PICK_UP_GESTURE_TRIGGERED),
+
+ @UiEvent(doc = ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+ FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN(1154,
+ ALTERNATE_BIOMETRIC_BOUNCER_SHOWN),
+
+ @UiEvent(doc = PRIMARY_BOUNCER_SHOWN)
+ FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN(1155, PRIMARY_BOUNCER_SHOWN),
+
+ @UiEvent(doc = PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN)
+ FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN(
+ 1197,
+ PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
+ ),
+
+ @UiEvent(doc = RETRY_AFTER_HW_UNAVAILABLE)
+ FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE(1156, RETRY_AFTER_HW_UNAVAILABLE),
+
+ @UiEvent(doc = TRUST_DISABLED)
+ FACE_AUTH_TRIGGERED_TRUST_DISABLED(1158, TRUST_DISABLED),
+
+ @UiEvent(doc = TRUST_ENABLED)
+ FACE_AUTH_STOPPED_TRUST_ENABLED(1173, TRUST_ENABLED),
+
+ @UiEvent(doc = KEYGUARD_OCCLUSION_CHANGED)
+ FACE_AUTH_UPDATED_KEYGUARD_OCCLUSION_CHANGED(1159, KEYGUARD_OCCLUSION_CHANGED),
+
+ @UiEvent(doc = ASSISTANT_VISIBILITY_CHANGED)
+ FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED(1160, ASSISTANT_VISIBILITY_CHANGED),
+
+ @UiEvent(doc = STARTED_WAKING_UP)
+ FACE_AUTH_UPDATED_STARTED_WAKING_UP(1161, STARTED_WAKING_UP),
+
+ @UiEvent(doc = DREAM_STOPPED)
+ FACE_AUTH_TRIGGERED_DREAM_STOPPED(1162, DREAM_STOPPED),
+
+ @UiEvent(doc = ALL_AUTHENTICATORS_REGISTERED)
+ FACE_AUTH_TRIGGERED_ALL_AUTHENTICATORS_REGISTERED(1163, ALL_AUTHENTICATORS_REGISTERED),
+
+ @UiEvent(doc = ENROLLMENTS_CHANGED)
+ FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED(1164, ENROLLMENTS_CHANGED),
+
+ @UiEvent(doc = KEYGUARD_VISIBILITY_CHANGED)
+ FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED(1165, KEYGUARD_VISIBILITY_CHANGED),
+
+ @UiEvent(doc = FACE_CANCEL_NOT_RECEIVED)
+ FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED(1174, FACE_CANCEL_NOT_RECEIVED),
+
+ @UiEvent(doc = AUTH_REQUEST_DURING_CANCELLATION)
+ FACE_AUTH_TRIGGERED_DURING_CANCELLATION(1175, AUTH_REQUEST_DURING_CANCELLATION),
+
+ @UiEvent(doc = DREAM_STARTED)
+ FACE_AUTH_STOPPED_DREAM_STARTED(1176, DREAM_STARTED),
+
+ @UiEvent(doc = FP_LOCKED_OUT)
+ FACE_AUTH_STOPPED_FP_LOCKED_OUT(1177, FP_LOCKED_OUT),
+
+ @UiEvent(doc = FACE_AUTH_STOPPED_ON_USER_INPUT)
+ FACE_AUTH_STOPPED_USER_INPUT_ON_BOUNCER(1178, FACE_AUTH_STOPPED_ON_USER_INPUT),
+
+ @UiEvent(doc = KEYGUARD_GOING_AWAY)
+ FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY(1179, KEYGUARD_GOING_AWAY),
+
+ @UiEvent(doc = CAMERA_LAUNCHED)
+ FACE_AUTH_UPDATED_CAMERA_LAUNCHED(1180, CAMERA_LAUNCHED),
+
+ @UiEvent(doc = FP_AUTHENTICATED)
+ FACE_AUTH_UPDATED_FP_AUTHENTICATED(1181, FP_AUTHENTICATED),
+
+ @UiEvent(doc = GOING_TO_SLEEP)
+ FACE_AUTH_UPDATED_GOING_TO_SLEEP(1182, GOING_TO_SLEEP),
+
+ @UiEvent(doc = FINISHED_GOING_TO_SLEEP)
+ FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP(1183, FINISHED_GOING_TO_SLEEP),
+
+ @UiEvent(doc = KEYGUARD_INIT)
+ FACE_AUTH_UPDATED_ON_KEYGUARD_INIT(1189, KEYGUARD_INIT),
+
+ @UiEvent(doc = KEYGUARD_RESET)
+ FACE_AUTH_UPDATED_KEYGUARD_RESET(1185, KEYGUARD_RESET),
+
+ @UiEvent(doc = USER_SWITCHING)
+ FACE_AUTH_UPDATED_USER_SWITCHING(1186, USER_SWITCHING),
+
+ @UiEvent(doc = FACE_AUTHENTICATED)
+ FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED(1187, FACE_AUTHENTICATED),
+
+ @UiEvent(doc = BIOMETRIC_ENABLED)
+ FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD(1188, BIOMETRIC_ENABLED);
+
+ override fun getId(): Int = this.id
+}
+
+private val apiRequestReasonToUiEvent =
+ mapOf(
+ SWIPE_UP_ON_BOUNCER to FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ UDFPS_POINTER_DOWN to FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN,
+ NOTIFICATION_PANEL_CLICKED to
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED,
+ QS_EXPANDED to FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED,
+ PICK_UP_GESTURE_TRIGGERED to FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED,
+ )
+
+/** Converts the [reason] to the corresponding [FaceAuthUiEvent]. */
+fun apiRequestReasonToUiEvent(@FaceAuthApiRequestReason reason: String): FaceAuthUiEvent =
+ apiRequestReasonToUiEvent[reason]!!
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index e1fabde..d7cd1d0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -8,7 +8,6 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
@@ -68,6 +67,7 @@
private int mClockSwitchYAmount;
@VisibleForTesting boolean mChildrenAreLaidOut = false;
+ @VisibleForTesting boolean mAnimateOnLayout = true;
public KeyguardClockSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -105,9 +105,7 @@
}
// Attach small and big clock views to hierarchy.
- mSmallClockFrame.addView(clock.getSmallClock(), -1,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ mSmallClockFrame.addView(clock.getSmallClock());
mLargeClockFrame.addView(clock.getLargeClock());
}
@@ -214,7 +212,7 @@
super.onLayout(changed, l, t, r, b);
if (mDisplayedClockSize != null && !mChildrenAreLaidOut) {
- post(() -> updateClockViews(mDisplayedClockSize == LARGE, /* animate */ true));
+ post(() -> updateClockViews(mDisplayedClockSize == LARGE, mAnimateOnLayout));
}
mChildrenAreLaidOut = true;
@@ -222,7 +220,7 @@
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardClockSwitch:");
- pw.println(" mClockFrame: " + mSmallClockFrame);
+ pw.println(" mSmallClockFrame: " + mSmallClockFrame);
pw.println(" mLargeClockFrame: " + mLargeClockFrame);
pw.println(" mStatusArea: " + mStatusArea);
pw.println(" mDisplayedClockSize: " + mDisplayedClockSize);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index dd78f1c..2165099 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -72,7 +72,6 @@
private final DumpManager mDumpManager;
private final ClockEventController mClockEventController;
- /** Clock frames for both small and large sizes */
private FrameLayout mSmallClockFrame; // top aligned clock
private FrameLayout mLargeClockFrame; // centered clock
@@ -151,7 +150,7 @@
* Attach the controller to the view it relates to.
*/
@Override
- public void onInit() {
+ protected void onInit() {
mKeyguardSliceViewController.init();
mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
@@ -390,8 +389,6 @@
* bounds during the unlock transition.
*/
private void setClipChildrenForUnlock(boolean clip) {
- mView.setClipChildren(clip);
-
if (mStatusArea != null) {
mStatusArea.setClipChildren(clip);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 87300c3..50c9193 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -45,7 +45,6 @@
private final EmergencyButtonController mEmergencyButtonController;
private boolean mPaused;
-
// The following is used to ignore callbacks from SecurityViews that are no longer current
// (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
// state for the current security method.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 0ea1965..919b71b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -52,6 +52,7 @@
val becauseCannotSkipBouncer: Boolean,
val biometricSettingEnabledForUser: Boolean,
val bouncerFullyShown: Boolean,
+ val bouncerIsOrWillShow: Boolean,
val faceAuthenticated: Boolean,
val faceDisabled: Boolean,
val goingToSleep: Boolean,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 6844b65..20fa8f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -16,23 +16,25 @@
package com.android.keyguard;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.view.View;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
-import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
/**
@@ -40,7 +42,7 @@
*/
public class KeyguardPINView extends KeyguardPinBasedInputView {
- private final AppearAnimationUtils mAppearAnimationUtils;
+ ValueAnimator mAppearAnimator = ValueAnimator.ofFloat(0f, 1f);
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
private ConstraintLayout mContainer;
@@ -54,7 +56,6 @@
public KeyguardPINView(Context context, AttributeSet attrs) {
super(context, attrs);
- mAppearAnimationUtils = new AppearAnimationUtils(context);
mDisappearAnimationUtils = new DisappearAnimationUtils(context,
125, 0.6f /* translationScale */,
0.45f /* delayScale */, AnimationUtils.loadInterpolator(
@@ -169,25 +170,20 @@
@Override
public void startAppearAnimation() {
- enableClipping(false);
- setAlpha(1f);
- setTranslationY(mAppearAnimationUtils.getStartTranslation());
- AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
- 0, mAppearAnimationUtils.getInterpolator(),
- getAnimationListener(CUJ_LOCKSCREEN_PIN_APPEAR));
- mAppearAnimationUtils.startAnimation2d(mViews,
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- }
- });
+ if (mAppearAnimator.isRunning()) {
+ mAppearAnimator.cancel();
+ }
+ mAppearAnimator.setDuration(650);
+ mAppearAnimator.addUpdateListener(animation -> animate(animation.getAnimatedFraction()));
+ mAppearAnimator.start();
}
public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
final Runnable finishRunnable) {
+ if (mAppearAnimator.isRunning()) {
+ mAppearAnimator.cancel();
+ }
- enableClipping(false);
setTranslationY(0);
DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
? mDisappearAnimationUtilsLocked
@@ -195,7 +191,6 @@
disappearAnimationUtils.createAnimation(
this, 0, 200, mDisappearYTranslation, false,
mDisappearAnimationUtils.getInterpolator(), () -> {
- enableClipping(true);
if (finishRunnable != null) {
finishRunnable.run();
}
@@ -204,14 +199,32 @@
return true;
}
- private void enableClipping(boolean enable) {
- mContainer.setClipToPadding(enable);
- mContainer.setClipChildren(enable);
- setClipChildren(enable);
- }
-
@Override
public boolean hasOverlappingRendering() {
return false;
}
+
+ /** Animate subviews according to expansion or time. */
+ private void animate(float progress) {
+ for (int i = 0; i < mViews.length; i++) {
+ View[] row = mViews[i];
+ for (View view : row) {
+ if (view == null) {
+ continue;
+ }
+
+ float scaledProgress = MathUtils.constrain(
+ (progress - 0.075f * i) / (1f - 0.075f * mViews.length),
+ 0f,
+ 1f
+ );
+ view.setAlpha(scaledProgress);
+ Interpolator interpolator = Interpolators.STANDARD_ACCELERATE;
+ view.setTranslationY(40 - (40 * interpolator.getInterpolation(scaledProgress)));
+ if (view instanceof NumPadAnimationListener) {
+ ((NumPadAnimationListener) view).setProgress(scaledProgress);
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 93175e1..57058b7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -223,7 +223,7 @@
@Override
public void onSwipeUp() {
if (!mUpdateMonitor.isFaceDetectionRunning()) {
- mUpdateMonitor.requestFaceAuth(true);
+ mUpdateMonitor.requestFaceAuth(true, FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER);
mKeyguardSecurityCallback.userActivity();
showMessage(null, null);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 3aa5ada..51b68b7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -156,7 +156,6 @@
@Override
public void onStartingToHide() {
-
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 98bc28d..094c4a7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.ACTION_USER_REMOVED;
@@ -32,11 +33,43 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.keyguard.FaceAuthReasonKt.apiRequestReasonToUiEvent;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_DREAM_STARTED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FP_LOCKED_OUT;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_TRUST_ENABLED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_USER_INPUT_ON_BOUNCER;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALL_AUTHENTICATORS_REGISTERED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_DREAM_STOPPED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_DURING_CANCELLATION;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ON_REACH_GESTURE_ON_AOD;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_TRUST_DISABLED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_CAMERA_LAUNCHED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_FP_AUTHENTICATED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_GOING_TO_SLEEP;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_OCCLUSION_CHANGED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_RESET;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_KEYGUARD_INIT;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
@@ -92,6 +125,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
@@ -105,6 +140,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -130,6 +166,7 @@
import java.util.function.Consumer;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Watches for updates that may be interesting to the keyguard, and provides
@@ -225,6 +262,7 @@
private final boolean mIsPrimaryUser;
private final AuthController mAuthController;
private final StatusBarStateController mStatusBarStateController;
+ private final UiEventLogger mUiEventLogger;
private int mStatusBarState;
private final StatusBarStateController.StateListener mStatusBarStateControllerListener =
new StatusBarStateController.StateListener() {
@@ -290,7 +328,6 @@
private KeyguardBypassController mKeyguardBypassController;
private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
- private boolean mIsFaceAuthUserRequested;
private LockPatternUtils mLockPatternUtils;
private final IDreamManager mDreamManager;
private boolean mIsDreaming;
@@ -322,6 +359,7 @@
protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived;
+ private final Provider<SessionTracker> mSessionTrackerProvider;
@VisibleForTesting
protected Handler getHandler() {
@@ -337,7 +375,8 @@
public void onChanged(boolean enabled, int userId) throws RemoteException {
mHandler.post(() -> {
mBiometricEnabledForUser.put(userId, enabled);
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD);
});
}
};
@@ -405,9 +444,11 @@
// authenticating. TrustManager sends an onTrustChanged whenever a user unlocks keyguard,
// for this reason we need to make sure to not authenticate.
if (wasTrusted == enabled || enabled) {
- updateBiometricListeningState(BIOMETRIC_ACTION_STOP);
+ updateBiometricListeningState(BIOMETRIC_ACTION_STOP,
+ FACE_AUTH_STOPPED_TRUST_ENABLED);
} else {
- updateBiometricListeningState(BIOMETRIC_ACTION_START);
+ updateBiometricListeningState(BIOMETRIC_ACTION_START,
+ FACE_AUTH_TRIGGERED_TRUST_DISABLED);
}
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -601,7 +642,7 @@
public void setKeyguardGoingAway(boolean goingAway) {
mKeyguardGoingAway = goingAway;
// This is set specifically to stop face authentication from running.
- updateBiometricListeningState(BIOMETRIC_ACTION_STOP);
+ updateBiometricListeningState(BIOMETRIC_ACTION_STOP, FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY);
}
/**
@@ -609,7 +650,8 @@
*/
public void setKeyguardOccluded(boolean occluded) {
mKeyguardOccluded = occluded;
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_KEYGUARD_OCCLUSION_CHANGED);
}
@@ -621,7 +663,8 @@
*/
public void requestFaceAuthOnOccludingApp(boolean request) {
mOccludingAppRequestingFace = request;
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED);
}
/**
@@ -640,7 +683,8 @@
*/
public void onCameraLaunched() {
mSecureCameraLaunched = true;
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_CAMERA_LAUNCHED);
}
/**
@@ -682,7 +726,8 @@
}
// Don't send cancel if authentication succeeds
mFingerprintCancelSignal = null;
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_FP_AUTHENTICATED);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -845,7 +890,7 @@
if (isUdfpsEnrolled()) {
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
- stopListeningForFace();
+ stopListeningForFace(FACE_AUTH_STOPPED_FP_LOCKED_OUT);
}
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -924,7 +969,8 @@
}
// Don't send cancel if authentication succeeds
mFaceCancelSignal = null;
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1013,14 +1059,16 @@
@Override
public void run() {
mLogger.logRetryingAfterFaceHwUnavailable(mHardwareFaceUnavailableRetryCount);
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE);
}
};
private void onFaceCancelNotReceived() {
mLogger.e("Face cancellation not received, transitioning to STOPPED");
mFaceRunningState = BIOMETRIC_STATE_STOPPED;
- KeyguardUpdateMonitor.this.updateFaceListeningState(BIOMETRIC_ACTION_STOP);
+ KeyguardUpdateMonitor.this.updateFaceListeningState(BIOMETRIC_ACTION_STOP,
+ FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED);
}
private void handleFaceError(int msgId, final String originalErrMsg) {
@@ -1043,7 +1091,8 @@
if (msgId == FaceManager.FACE_ERROR_CANCELED
&& mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_DURING_CANCELLATION);
} else {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
}
@@ -1089,7 +1138,8 @@
final boolean changed = (mFaceLockedOutPermanent != wasLockoutPermanent);
mHandler.postDelayed(() -> {
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET);
}, getBiometricLockoutDelay());
if (changed) {
@@ -1334,7 +1384,8 @@
@VisibleForTesting
void setAssistantVisible(boolean assistantVisible) {
mAssistantVisible = assistantVisible;
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED);
if (mAssistantVisible) {
requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.ASSISTANT,
@@ -1512,7 +1563,7 @@
@Override
public void onUdfpsPointerDown(int sensorId) {
mLogger.logUdfpsPointerDown(sensorId);
- requestFaceAuth(true);
+ requestFaceAuth(true, FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
}
/**
@@ -1696,7 +1747,7 @@
protected void handleStartedWakingUp() {
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
Assert.isMainThread();
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_STARTED_WAKING_UP);
requestActiveUnlock(ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE, "wakingUp");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1717,7 +1768,7 @@
}
}
mGoingToSleep = true;
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_GOING_TO_SLEEP);
}
protected void handleFinishedGoingToSleep(int arg1) {
@@ -1730,7 +1781,8 @@
}
}
// This is set specifically to stop face authentication from running.
- updateBiometricListeningState(BIOMETRIC_ACTION_STOP);
+ updateBiometricListeningState(BIOMETRIC_ACTION_STOP,
+ FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP);
}
private void handleScreenTurnedOff() {
@@ -1750,9 +1802,10 @@
}
if (mIsDreaming) {
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
- updateFaceListeningState(BIOMETRIC_ACTION_STOP);
+ updateFaceListeningState(BIOMETRIC_ACTION_STOP, FACE_AUTH_STOPPED_DREAM_STARTED);
} else {
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_DREAM_STOPPED);
}
}
@@ -1817,8 +1870,10 @@
InteractionJankMonitor interactionJankMonitor,
LatencyTracker latencyTracker,
ActiveUnlockConfig activeUnlockConfiguration,
- KeyguardUpdateMonitorLogger logger) {
- mLogger = logger;
+ KeyguardUpdateMonitorLogger logger,
+ UiEventLogger uiEventLogger,
+ // This has to be a provider because SessionTracker depends on KeyguardUpdateMonitor :(
+ Provider<SessionTracker> sessionTrackerProvider) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
mTelephonyListenerManager = telephonyListenerManager;
@@ -1836,6 +1891,9 @@
dumpManager.registerDumpable(getClass().getName(), this);
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
mActiveUnlockConfig = activeUnlockConfiguration;
+ mLogger = logger;
+ mUiEventLogger = uiEventLogger;
+ mSessionTrackerProvider = sessionTrackerProvider;
mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
mHandler = new Handler(mainLooper) {
@@ -1919,7 +1977,8 @@
setAssistantVisible((boolean) msg.obj);
break;
case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_FP_AUTHENTICATED);
break;
case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
updateLogoutEnabled();
@@ -2022,15 +2081,17 @@
@Override
public void onAllAuthenticatorsRegistered(
@BiometricAuthenticator.Modality int modality) {
- mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE));
+ mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_ALL_AUTHENTICATORS_REGISTERED));
}
@Override
public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
- mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE));
+ mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED));
}
});
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_KEYGUARD_INIT);
if (mFpm != null) {
mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
}
@@ -2142,9 +2203,10 @@
mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
}
- private void updateBiometricListeningState(int action) {
+ private void updateBiometricListeningState(int action,
+ @NonNull FaceAuthUiEvent faceAuthUiEvent) {
updateFingerprintListeningState(action);
- updateFaceListeningState(action);
+ updateFaceListeningState(action, faceAuthUiEvent);
}
private void updateFingerprintListeningState(int action) {
@@ -2200,7 +2262,8 @@
return;
}
mAuthInterruptActive = active;
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_ON_REACH_GESTURE_ON_AOD);
requestActiveUnlock(ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE, "onReach");
}
@@ -2208,25 +2271,27 @@
* Requests face authentication if we're on a state where it's allowed.
* This will re-trigger auth in case it fails.
* @param userInitiatedRequest true if the user explicitly requested face auth
+ * @param reason One of the reasons {@link FaceAuthApiRequestReason} on why this API is being
+ * invoked.
*/
- public void requestFaceAuth(boolean userInitiatedRequest) {
+ public void requestFaceAuth(boolean userInitiatedRequest,
+ @FaceAuthApiRequestReason String reason) {
mLogger.logFaceAuthRequested(userInitiatedRequest);
- mIsFaceAuthUserRequested |= userInitiatedRequest;
- updateFaceListeningState(BIOMETRIC_ACTION_START);
+ updateFaceListeningState(BIOMETRIC_ACTION_START, apiRequestReasonToUiEvent(reason));
}
/**
* In case face auth is running, cancel it.
*/
public void cancelFaceAuth() {
- stopListeningForFace();
+ stopListeningForFace(FACE_AUTH_STOPPED_USER_INPUT_ON_BOUNCER);
}
public boolean isFaceScanning() {
return mFaceRunningState == BIOMETRIC_STATE_RUNNING;
}
- private void updateFaceListeningState(int action) {
+ private void updateFaceListeningState(int action, @NonNull FaceAuthUiEvent faceAuthUiEvent) {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
@@ -2239,17 +2304,21 @@
mLogger.v("Ignoring stopListeningForFace()");
return;
}
- mIsFaceAuthUserRequested = false;
- stopListeningForFace();
+ stopListeningForFace(faceAuthUiEvent);
} else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING && shouldListenForFace) {
if (action == BIOMETRIC_ACTION_STOP) {
mLogger.v("Ignoring startListeningForFace()");
return;
}
- startListeningForFace();
+ startListeningForFace(faceAuthUiEvent);
}
}
+ @Nullable
+ private InstanceId getKeyguardSessionId() {
+ return mSessionTrackerProvider.get().getSessionId(SESSION_KEYGUARD);
+ }
+
/**
* Initiates active unlock to get the unlock token ready.
*/
@@ -2318,7 +2387,8 @@
public void setUdfpsBouncerShowing(boolean showing) {
mUdfpsBouncerShowing = showing;
if (mUdfpsBouncerShowing) {
- updateFaceListeningState(BIOMETRIC_ACTION_START);
+ updateFaceListeningState(BIOMETRIC_ACTION_START,
+ FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN);
requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT,
"udfpsBouncer");
@@ -2480,7 +2550,7 @@
// on bouncer if both fp and fingerprint are enrolled.
final boolean awakeKeyguardExcludingBouncerShowing = mKeyguardIsVisible
&& mDeviceInteractive && !mGoingToSleep
- && !statusBarShadeLocked && !mBouncerFullyShown;
+ && !statusBarShadeLocked && !mBouncerIsOrWillBeShowing;
final int user = getCurrentUser();
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
final boolean isLockDown =
@@ -2549,6 +2619,7 @@
becauseCannotSkipBouncer,
biometricEnabledForUser,
mBouncerFullyShown,
+ mBouncerIsOrWillBeShowing,
faceAuthenticated,
faceDisabledForUser,
mGoingToSleep,
@@ -2630,7 +2701,7 @@
}
}
- private void startListeningForFace() {
+ private void startListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) {
final int userId = getCurrentUser();
final boolean unlockPossible = isUnlockWithFacePossible(userId);
if (mFaceCancelSignal != null) {
@@ -2644,7 +2715,8 @@
// Waiting for ERROR_CANCELED before requesting auth again
return;
}
- mLogger.logStartedListeningForFace(mFaceRunningState);
+ mLogger.logStartedListeningForFace(mFaceRunningState, faceAuthUiEvent.getReason());
+ mUiEventLogger.log(faceAuthUiEvent, getKeyguardSessionId());
if (unlockPossible) {
mFaceCancelSignal = new CancellationSignal();
@@ -2728,8 +2800,10 @@
}
}
- private void stopListeningForFace() {
+ private void stopListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) {
mLogger.v("stopListeningForFace()");
+ mLogger.logStoppedListeningForFace(mFaceRunningState, faceAuthUiEvent.getReason());
+ mUiEventLogger.log(faceAuthUiEvent, getKeyguardSessionId());
if (mFaceRunningState == BIOMETRIC_STATE_RUNNING) {
if (mFaceCancelSignal != null) {
mFaceCancelSignal.cancel();
@@ -3058,7 +3132,8 @@
cb.onKeyguardVisibilityChangedRaw(showing);
}
}
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED);
}
/**
@@ -3066,7 +3141,8 @@
*/
private void handleKeyguardReset() {
mLogger.d("handleKeyguardReset");
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_KEYGUARD_RESET);
mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
}
@@ -3117,7 +3193,8 @@
cb.onKeyguardBouncerStateChanged(mBouncerIsOrWillBeShowing);
}
}
- updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN);
}
if (wasBouncerFullyShown != mBouncerFullyShown) {
@@ -3132,7 +3209,8 @@
cb.onKeyguardBouncerFullyShowingChanged(mBouncerFullyShown);
}
}
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN);
}
}
@@ -3260,7 +3338,8 @@
mSwitchingUser = switching;
// Since this comes in on a binder thread, we need to post if first
mHandler.post(() -> {
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_USER_SWITCHING);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt
similarity index 61%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
copy to packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt
index e62a63a..f449edf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt
@@ -14,18 +14,10 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode;
+package com.android.keyguard
-import android.os.SystemProperties;
-
-/**
- * Constants for desktop mode feature
- */
-public class DesktopModeConstants {
-
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+/** Interface for classes to track animation progress. */
+interface NumPadAnimationListener {
+ /** Track the progress of the animation. */
+ fun setProgress(progress: Float)
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index c91c899..e0cafae 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -48,6 +48,10 @@
private int mTextColorPrimary;
private int mTextColorPressed;
private int mStyle;
+ private float mStartRadius;
+ private float mEndRadius;
+ private int mHeight;
+
private static final int EXPAND_ANIMATION_MS = 100;
private static final int EXPAND_COLOR_ANIMATION_MS = 50;
private static final int CONTRACT_ANIMATION_DELAY_MS = 33;
@@ -80,12 +84,20 @@
mContractAnimatorSet.start();
}
+ public void setProgress(float progress) {
+ mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress);
+ int height = (int) (mHeight * 0.8f + mHeight * 0.2 * progress);
+ int difference = mHeight - height;
+ mBackground.setBounds(0, difference / 2, mHeight, mHeight - difference / 2);
+ }
+
void onLayout(int height) {
- float startRadius = height / 2f;
- float endRadius = height / 4f;
- mBackground.setCornerRadius(startRadius);
- mExpandAnimator.setFloatValues(startRadius, endRadius);
- mContractAnimator.setFloatValues(endRadius, startRadius);
+ mHeight = height;
+ mStartRadius = height / 2f;
+ mEndRadius = height / 4f;
+ mBackground.setCornerRadius(mStartRadius);
+ mExpandAnimator.setFloatValues(mStartRadius, mEndRadius);
+ mContractAnimator.setFloatValues(mEndRadius, mStartRadius);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 8099f75..37060987c 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -30,7 +30,7 @@
/**
* Similar to the {@link NumPadKey}, but displays an image.
*/
-public class NumPadButton extends AlphaOptimizedImageButton {
+public class NumPadButton extends AlphaOptimizedImageButton implements NumPadAnimationListener {
@Nullable
private NumPadAnimator mAnimator;
@@ -104,4 +104,11 @@
a.recycle();
((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
}
+
+ @Override
+ public void setProgress(float progress) {
+ if (mAnimator != null) {
+ mAnimator.setProgress(progress);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 4aed251..0a4880e 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -37,7 +37,10 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
-public class NumPadKey extends ViewGroup {
+/**
+ * Viewgroup for the bouncer numpad button, specifically for digits.
+ */
+public class NumPadKey extends ViewGroup implements NumPadAnimationListener {
// list of "ABC", etc per digit, starting with '0'
static String sKlondike[];
@@ -221,4 +224,11 @@
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
+
+ @Override
+ public void setProgress(float progress) {
+ if (mAnimator != null) {
+ mAnimator.setProgress(progress);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index d718a24..2bc98f1 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -262,10 +262,18 @@
logBuffer.log(TAG, VERBOSE, { int1 = subId }, { "reportSimUnlocked(subId=$int1)" })
}
- fun logStartedListeningForFace(faceRunningState: Int) {
- logBuffer.log(TAG, VERBOSE,
- { int1 = faceRunningState },
- { "startListeningForFace(): $int1" })
+ fun logStartedListeningForFace(faceRunningState: Int, faceAuthReason: String) {
+ logBuffer.log(TAG, VERBOSE, {
+ int1 = faceRunningState
+ str1 = faceAuthReason
+ }, { "startListeningForFace(): $int1, reason: $str1" })
+ }
+
+ fun logStoppedListeningForFace(faceRunningState: Int, faceAuthReason: String) {
+ logBuffer.log(TAG, VERBOSE, {
+ int1 = faceRunningState
+ str1 = faceAuthReason
+ }, { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" })
}
fun logSubInfo(subInfo: SubscriptionInfo?) {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 0cb4da4..7fc8123 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -85,8 +85,6 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -301,7 +299,6 @@
@Inject Lazy<IStatusBarService> mIStatusBarService;
@Inject Lazy<DisplayMetrics> mDisplayMetrics;
@Inject Lazy<LockscreenGestureLogger> mLockscreenGestureLogger;
- @Inject Lazy<KeyguardEnvironment> mKeyguardEnvironment;
@Inject Lazy<ShadeController> mShadeController;
@Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
@Inject Lazy<AppOpsController> mAppOpsController;
@@ -318,10 +315,8 @@
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
- @Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
- @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
@Inject Lazy<PrivacyItemController> mPrivacyItemController;
@Inject @Background Lazy<Looper> mBgLooper;
@Inject @Background Lazy<Handler> mBgHandler;
@@ -503,7 +498,6 @@
mProviders.put(LockscreenGestureLogger.class, mLockscreenGestureLogger::get);
- mProviders.put(KeyguardEnvironment.class, mKeyguardEnvironment::get);
mProviders.put(ShadeController.class, mShadeController::get);
mProviders.put(NotificationRemoteInputManager.Callback.class,
@@ -530,9 +524,6 @@
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
mRemoteInputQuickSettingsDisabler::get);
- mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
- mProviders.put(ForegroundServiceNotificationListener.class,
- mForegroundServiceNotificationListener::get);
mProviders.put(ClockManager.class, mClockManager::get);
mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 04e26d3..a1a3b72 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -23,14 +23,10 @@
import android.service.notification.StatusBarNotification;
import android.util.Log;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.util.time.SystemClock;
import javax.inject.Inject;
@@ -43,42 +39,20 @@
private final Context mContext;
private final ForegroundServiceController mForegroundServiceController;
- private final NotificationEntryManager mEntryManager;
+ private final NotifPipeline mNotifPipeline;
@Inject
public ForegroundServiceNotificationListener(Context context,
ForegroundServiceController foregroundServiceController,
- NotificationEntryManager notificationEntryManager,
- NotifPipeline notifPipeline,
- SystemClock systemClock) {
+ NotifPipeline notifPipeline) {
mContext = context;
mForegroundServiceController = foregroundServiceController;
+ mNotifPipeline = notifPipeline;
+ }
- // TODO: (b/145659174) remove mEntryManager when moving to NewNotifPipeline. Replaced by
- // ForegroundCoordinator
- mEntryManager = notificationEntryManager;
- mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
- @Override
- public void onPendingEntryAdded(NotificationEntry entry) {
- addNotification(entry, entry.getImportance());
- }
-
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- updateNotification(entry, entry.getImportance());
- }
-
- @Override
- public void onEntryRemoved(
- NotificationEntry entry,
- NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- removeNotification(entry.getSbn());
- }
- });
-
- notifPipeline.addCollectionListener(new NotifCollectionListener() {
+ /** Initializes this listener by connecting it to the notification pipeline. */
+ public void init() {
+ mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override
public void onEntryAdded(NotificationEntry entry) {
addNotification(entry, entry.getImportance());
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
index 40d1eff..e4c197f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
@@ -29,8 +29,9 @@
/** Face/Fingerprint combined icon animator for BiometricPrompt. */
class AuthBiometricFingerprintAndFaceIconController(
context: Context,
- iconView: LottieAnimationView
-) : AuthBiometricFingerprintIconController(context, iconView) {
+ iconView: LottieAnimationView,
+ iconViewOverlay: LottieAnimationView
+) : AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay) {
override val actsAsConfirmButton: Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
index 7371442..7f5a67f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
@@ -39,5 +39,5 @@
override fun onPointerDown(failedModalities: Set<Int>) = failedModalities.contains(TYPE_FACE)
override fun createIconController(): AuthIconController =
- AuthBiometricFingerprintAndFaceIconController(mContext, mIconView)
+ AuthBiometricFingerprintAndFaceIconController(mContext, mIconView, mIconViewOverlay)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 9b5f54a..4eaa1c6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -18,6 +18,10 @@
import android.annotation.RawRes
import android.content.Context
+import android.hardware.fingerprint.FingerprintManager
+import android.view.DisplayInfo
+import android.view.Surface
+import android.view.View
import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.BiometricState
@@ -32,14 +36,18 @@
/** Fingerprint only icon animator for BiometricPrompt. */
open class AuthBiometricFingerprintIconController(
context: Context,
- iconView: LottieAnimationView
+ iconView: LottieAnimationView,
+ protected val iconViewOverlay: LottieAnimationView
) : AuthIconController(context, iconView) {
+ private val isSideFps: Boolean
var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
set(value) {
if (field == value) {
return
}
+ iconViewOverlay.layoutParams.width = value.first
+ iconViewOverlay.layoutParams.height = value.second
iconView.layoutParams.width = value.first
iconView.layoutParams.height = value.second
field = value
@@ -50,9 +58,51 @@
R.dimen.biometric_dialog_fingerprint_icon_width),
context.resources.getDimensionPixelSize(
R.dimen.biometric_dialog_fingerprint_icon_height))
+ var sideFps = false
+ (context.getSystemService(Context.FINGERPRINT_SERVICE)
+ as FingerprintManager?)?.let { fpm ->
+ for (prop in fpm.sensorPropertiesInternal) {
+ if (prop.isAnySidefpsType) {
+ sideFps = true
+ }
+ }
+ }
+ isSideFps = sideFps
+ val displayInfo = DisplayInfo()
+ context.display?.getDisplayInfo(displayInfo)
+ if (isSideFps && displayInfo.rotation == Surface.ROTATION_180) {
+ iconView.rotation = 180f
+ }
}
- override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ val displayInfo = DisplayInfo()
+ context.display?.getDisplayInfo(displayInfo)
+ val rotation = displayInfo.rotation
+ val iconAnimation = getSideFpsAnimationForTransition(rotation)
+ val iconViewOverlayAnimation =
+ getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return
+
+ if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
+ iconView.setAnimation(iconAnimation)
+ iconViewOverlay.setAnimation(iconViewOverlayAnimation)
+ }
+
+ val iconContentDescription = getIconContentDescription(newState)
+ if (iconContentDescription != null) {
+ iconView.contentDescription = iconContentDescription
+ iconViewOverlay.contentDescription = iconContentDescription
+ }
+
+ iconView.frame = 0
+ iconViewOverlay.frame = 0
+ if (shouldAnimateForTransition(lastState, newState)) {
+ iconView.playAnimation()
+ iconViewOverlay.playAnimation()
+ }
+ }
+
+ private fun updateIconNormal(@BiometricState lastState: Int, @BiometricState newState: Int) {
val icon = getAnimationForTransition(lastState, newState) ?: return
if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
@@ -70,6 +120,15 @@
}
}
+ override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ if (isSideFps) {
+ updateIconSideFps(lastState, newState)
+ } else {
+ iconViewOverlay.visibility = View.GONE
+ updateIconNormal(lastState, newState)
+ }
+ }
+
private fun getIconContentDescription(@BiometricState newState: Int): CharSequence? {
val id = when (newState) {
STATE_IDLE,
@@ -125,4 +184,89 @@
}
return if (id != null) return id else null
}
+
+ @RawRes
+ private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
+ Surface.ROTATION_0 -> R.raw.BiometricPrompt_Landscape_Base
+ Surface.ROTATION_90 -> R.raw.BiometricPrompt_Portrait_Base_TopLeft
+ Surface.ROTATION_180 -> R.raw.BiometricPrompt_Landscape_Base
+ Surface.ROTATION_270 -> R.raw.BiometricPrompt_Portrait_Base_BottomRight
+ else -> R.raw.BiometricPrompt_Landscape_Base
+ }
+
+ @RawRes
+ private fun getSideFpsOverlayAnimationForTransition(
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int,
+ rotation: Int
+ ): Int? = when (newState) {
+ STATE_HELP,
+ STATE_ERROR -> {
+ when (rotation) {
+ Surface.ROTATION_0 -> R.raw.BiometricPrompt_Fingerprint_To_Error_Landscape
+ Surface.ROTATION_90 ->
+ R.raw.BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_TopLeft
+ Surface.ROTATION_180 ->
+ R.raw.BiometricPrompt_Fingerprint_To_Error_Landscape
+ Surface.ROTATION_270 ->
+ R.raw.BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_BottomRight
+ else -> R.raw.BiometricPrompt_Fingerprint_To_Error_Landscape
+ }
+ }
+ STATE_AUTHENTICATING_ANIMATING_IN,
+ STATE_AUTHENTICATING -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.BiometricPrompt_Symbol_Error_To_Fingerprint_Landscape
+ Surface.ROTATION_90 ->
+ R.raw.BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_TopLeft
+ Surface.ROTATION_180 ->
+ R.raw.BiometricPrompt_Symbol_Error_To_Fingerprint_Landscape
+ Surface.ROTATION_270 ->
+ R.raw.BiometricPrompt_Symbol_Error_To_Fingerprint_Portrait_BottomRight
+ else -> R.raw.BiometricPrompt_Symbol_Error_To_Fingerprint_Landscape
+ }
+ } else {
+ when (rotation) {
+ Surface.ROTATION_0 -> R.raw.BiometricPrompt_Fingerprint_To_Error_Landscape
+ Surface.ROTATION_90 ->
+ R.raw.BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_TopLeft
+ Surface.ROTATION_180 ->
+ R.raw.BiometricPrompt_Fingerprint_To_Error_Landscape
+ Surface.ROTATION_270 ->
+ R.raw.BiometricPrompt_Symbol_Fingerprint_To_Error_Portrait_BottomRight
+ else -> R.raw.BiometricPrompt_Fingerprint_To_Error_Landscape
+ }
+ }
+ }
+ STATE_AUTHENTICATED -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.BiometricPrompt_Symbol_Error_To_Success_Landscape
+ Surface.ROTATION_90 ->
+ R.raw.BiometricPrompt_Symbol_Error_To_Success_Portrait_TopLeft
+ Surface.ROTATION_180 ->
+ R.raw.BiometricPrompt_Symbol_Error_To_Success_Landscape
+ Surface.ROTATION_270 ->
+ R.raw.BiometricPrompt_Symbol_Error_To_Success_Portrait_BottomRight
+ else -> R.raw.BiometricPrompt_Symbol_Error_To_Success_Landscape
+ }
+ } else {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.BiometricPrompt_Symbol_Fingerprint_To_Success_Landscape
+ Surface.ROTATION_90 ->
+ R.raw.BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_TopLeft
+ Surface.ROTATION_180 ->
+ R.raw.BiometricPrompt_Symbol_Fingerprint_To_Success_Landscape
+ Surface.ROTATION_270 ->
+ R.raw.BiometricPrompt_Symbol_Fingerprint_To_Success_Portrait_BottomRight
+ else -> R.raw.BiometricPrompt_Symbol_Fingerprint_To_Success_Landscape
+ }
+ }
+ }
+ else -> null
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
index 9cce066..2066634 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
@@ -86,7 +86,7 @@
override fun supportsSmallDialog() = false
override fun createIconController(): AuthIconController =
- AuthBiometricFingerprintIconController(mContext, mIconView)
+ AuthBiometricFingerprintIconController(mContext, mIconView, mIconViewOverlay)
fun updateOverrideIconLayoutParamsSize() {
udfpsAdapter?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index fc5cf9f..e94b1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -133,6 +133,7 @@
private TextView mSubtitleView;
private TextView mDescriptionView;
private View mIconHolderView;
+ protected LottieAnimationView mIconViewOverlay;
protected LottieAnimationView mIconView;
protected TextView mIndicatorView;
@@ -651,6 +652,7 @@
mTitleView = findViewById(R.id.title);
mSubtitleView = findViewById(R.id.subtitle);
mDescriptionView = findViewById(R.id.description);
+ mIconViewOverlay = findViewById(R.id.biometric_icon_overlay);
mIconView = findViewById(R.id.biometric_icon);
mIconHolderView = findViewById(R.id.biometric_icon_frame);
mIndicatorView = findViewById(R.id.indicator);
@@ -689,6 +691,11 @@
mIconController = createIconController();
if (mIconController.getActsAsConfirmButton()) {
+ mIconViewOverlay.setOnClickListener((view)->{
+ if (mState == STATE_PENDING_CONFIRMATION) {
+ updateState(STATE_AUTHENTICATED);
+ }
+ });
mIconView.setOnClickListener((view) -> {
if (mState == STATE_PENDING_CONFIRMATION) {
updateState(STATE_AUTHENTICATED);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 35269bc..163e3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -78,6 +78,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
@@ -160,6 +161,19 @@
private final @Background DelayableExecutor mBackgroundExecutor;
private final DisplayInfo mCachedDisplayInfo = new DisplayInfo();
+
+ private final VibratorHelper mVibratorHelper;
+
+ private void vibrateSuccess(int modality) {
+ mVibratorHelper.vibrateAuthSuccess(
+ getClass().getSimpleName() + ", modality = " + modality + "BP::success");
+ }
+
+ private void vibrateError(int modality) {
+ mVibratorHelper.vibrateAuthError(
+ getClass().getSimpleName() + ", modality = " + modality + "BP::error");
+ }
+
@VisibleForTesting
final TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
@@ -664,7 +678,8 @@
@NonNull StatusBarStateController statusBarStateController,
@NonNull InteractionJankMonitor jankMonitor,
@Main Handler handler,
- @Background DelayableExecutor bgExecutor) {
+ @Background DelayableExecutor bgExecutor,
+ @NonNull VibratorHelper vibrator) {
super(context);
mExecution = execution;
mUserManager = userManager;
@@ -681,6 +696,7 @@
mWindowManager = windowManager;
mInteractionJankMonitor = jankMonitor;
mUdfpsEnrolledForUser = new SparseBooleanArray();
+ mVibratorHelper = vibrator;
mOrientationListener = new BiometricDisplayListener(
context,
@@ -890,6 +906,8 @@
public void onBiometricAuthenticated(@Modality int modality) {
if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: ");
+ vibrateSuccess(modality);
+
if (mCurrentDialog != null) {
mCurrentDialog.onAuthenticationSucceeded(modality);
} else {
@@ -937,6 +955,8 @@
Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode));
}
+ vibrateError(modality);
+
final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
|| (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
new file mode 100644
index 0000000..f2d8aaa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.biometrics
+
+/**
+ * Provides whether an acquired error message should be shown immediately when its received (see
+ * [shouldDefer]) or should be shown when the biometric error is received [getDeferredMessage].
+ * @property excludedMessages messages that are excluded from counts
+ * @property messagesToDefer messages that shouldn't show immediately when received, but may be
+ * shown later if the message is the most frequent message processed and meets [THRESHOLD]
+ * percentage of all messages (excluding [excludedMessages])
+ */
+class BiometricMessageDeferral(
+ private val excludedMessages: Set<Int>,
+ private val messagesToDefer: Set<Int>
+) {
+ private val msgCounts: MutableMap<Int, Int> = HashMap() // msgId => frequency of msg
+ private val msgIdToCharSequence: MutableMap<Int, CharSequence> = HashMap() // msgId => message
+ private var totalRelevantMessages = 0
+ private var mostFrequentMsgIdToDefer: Int? = null
+
+ /** Reset all saved counts. */
+ fun reset() {
+ totalRelevantMessages = 0
+ msgCounts.clear()
+ msgIdToCharSequence.clear()
+ }
+
+ /** Whether the given message should be deferred instead of being shown immediately. */
+ fun shouldDefer(acquiredMsgId: Int): Boolean {
+ return messagesToDefer.contains(acquiredMsgId)
+ }
+
+ /**
+ * Adds the acquiredMsgId to the counts if it's not in [excludedMessages]. We still count
+ * messages that shouldn't be deferred in these counts.
+ */
+ fun processMessage(acquiredMsgId: Int, helpString: CharSequence) {
+ if (excludedMessages.contains(acquiredMsgId)) {
+ return
+ }
+
+ totalRelevantMessages++
+ msgIdToCharSequence[acquiredMsgId] = helpString
+
+ val newAcquiredMsgCount = msgCounts.getOrDefault(acquiredMsgId, 0) + 1
+ msgCounts[acquiredMsgId] = newAcquiredMsgCount
+ if (
+ messagesToDefer.contains(acquiredMsgId) &&
+ (mostFrequentMsgIdToDefer == null ||
+ newAcquiredMsgCount > msgCounts.getOrDefault(mostFrequentMsgIdToDefer!!, 0))
+ ) {
+ mostFrequentMsgIdToDefer = acquiredMsgId
+ }
+ }
+
+ /**
+ * Get the most frequent deferred message that meets the [THRESHOLD] percentage of processed
+ * messages excluding [excludedMessages].
+ * @return null if no messages have been deferred OR deferred messages didn't meet the
+ * [THRESHOLD] percentage of messages to show.
+ */
+ fun getDeferredMessage(): CharSequence? {
+ mostFrequentMsgIdToDefer?.let {
+ if (msgCounts.getOrDefault(it, 0) > (THRESHOLD * totalRelevantMessages)) {
+ return msgIdToCharSequence[mostFrequentMsgIdToDefer]
+ }
+ }
+
+ return null
+ }
+ companion object {
+ const val THRESHOLD = .5f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index d1589b2..7f2680b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -52,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.biometrics.dagger.BiometricsBackground;
@@ -852,7 +853,9 @@
playStartHaptic();
if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
- mKeyguardUpdateMonitor.requestFaceAuth(/* userInitiatedRequest */ false);
+ mKeyguardUpdateMonitor.requestFaceAuth(
+ /* userInitiatedRequest */ false,
+ FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
}
}
mOnFingerDown = true;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index d96476f..49e378e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -99,11 +99,12 @@
mProgressColor = context.getColor(R.color.udfps_enroll_progress);
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
mIsAccessibilityEnabled = am.isTouchExplorationEnabled();
- mOnFirstBucketFailedColor = context.getColor(R.color.udfps_moving_target_fill_error);
if (!mIsAccessibilityEnabled) {
mHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
+ mOnFirstBucketFailedColor = context.getColor(R.color.udfps_moving_target_fill_error);
} else {
mHelpColor = context.getColor(R.color.udfps_enroll_progress_help_with_talkback);
+ mOnFirstBucketFailedColor = mHelpColor;
}
mCheckmarkDrawable = context.getDrawable(R.drawable.udfps_enroll_checkmark);
mCheckmarkDrawable.mutate();
@@ -166,8 +167,7 @@
}
private void updateProgress(int remainingSteps, int totalSteps, boolean showingHelp) {
- if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps
- && mShowingHelp == showingHelp) {
+ if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps) {
return;
}
@@ -197,7 +197,6 @@
}
}
- mShowingHelp = showingHelp;
mRemainingSteps = remainingSteps;
mTotalSteps = totalSteps;
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 0b65966..6c45af2 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -24,11 +24,15 @@
* [Icon.Resource] to a resource.
*/
sealed class Icon {
+ abstract val contentDescription: ContentDescription?
+
data class Loaded(
val drawable: Drawable,
+ override val contentDescription: ContentDescription?,
) : Icon()
data class Resource(
@DrawableRes val res: Int,
+ override val contentDescription: ContentDescription?,
) : Icon()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/packages/SystemUI/src/com/android/systemui/common/shared/model/Text.kt
similarity index 62%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
copy to packages/SystemUI/src/com/android/systemui/common/shared/model/Text.kt
index e62a63a..5d0e08f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Text.kt
@@ -12,20 +12,23 @@
* WITHOUT WARRANTIES OR CONDITIONS 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;
+package com.android.systemui.common.shared.model
-import android.os.SystemProperties;
+import android.annotation.StringRes
/**
- * Constants for desktop mode feature
+ * Models a text, that can either be already [loaded][Text.Loaded] or be a [reference]
+ * [Text.Resource] to a resource.
*/
-public class DesktopModeConstants {
+sealed class Text {
+ data class Loaded(
+ val text: String?,
+ ) : Text()
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+ data class Resource(
+ @StringRes val res: Int,
+ ) : Text()
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
index d6433aa..93ae637 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
@@ -21,14 +21,16 @@
object ContentDescriptionViewBinder {
fun bind(
- contentDescription: ContentDescription,
+ contentDescription: ContentDescription?,
view: View,
) {
- when (contentDescription) {
- is ContentDescription.Loaded -> view.contentDescription = contentDescription.description
- is ContentDescription.Resource -> {
- view.contentDescription = view.context.resources.getString(contentDescription.res)
+ view.contentDescription =
+ when (contentDescription) {
+ null -> null
+ is ContentDescription.Loaded -> contentDescription.description
+ is ContentDescription.Resource -> {
+ view.context.resources.getString(contentDescription.res)
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
index aecee2a..108e22b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
@@ -24,6 +24,7 @@
icon: Icon,
view: ImageView,
) {
+ ContentDescriptionViewBinder.bind(icon.contentDescription, view)
when (icon) {
is Icon.Loaded -> view.setImageDrawable(icon.drawable)
is Icon.Resource -> view.setImageResource(icon.res)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TextViewBinder.kt
similarity index 60%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
copy to packages/SystemUI/src/com/android/systemui/common/ui/binder/TextViewBinder.kt
index e62a63a..396e8bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TextViewBinder.kt
@@ -12,20 +12,20 @@
* WITHOUT WARRANTIES OR CONDITIONS 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;
+package com.android.systemui.common.ui.binder
-import android.os.SystemProperties;
+import android.widget.TextView
+import com.android.systemui.common.shared.model.Text
-/**
- * Constants for desktop mode feature
- */
-public class DesktopModeConstants {
-
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+object TextViewBinder {
+ fun bind(view: TextView, viewModel: Text) {
+ view.text =
+ when (viewModel) {
+ is Text.Resource -> view.context.getString(viewModel.res)
+ is Text.Loaded -> viewModel.text
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 91e75f6..822f8f2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -33,6 +33,12 @@
fun hide()
/**
+ * Returns the preferred activity to start, depending on if the user has favorited any
+ * controls.
+ */
+ fun resolveActivity(): Class<*>
+
+ /**
* Request all open dialogs be closed. Set [immediately] to true to dismiss without
* animations.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index c1e99f5..bf7d716 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -149,6 +149,19 @@
}
}
+ override fun resolveActivity(): Class<*> {
+ val allStructures = controlsController.get().getFavorites()
+ val selectedStructure = getPreferredStructure(allStructures)
+
+ return if (controlsController.get().addSeedingFavoritesCallback(onSeedingComplete)) {
+ ControlsActivity::class.java
+ } else if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
+ ControlsProviderSelectorActivity::class.java
+ } else {
+ ControlsActivity::class.java
+ }
+ }
+
override fun show(
parent: ViewGroup,
onDismiss: Runnable,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 4157728..fbfc94a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -54,13 +54,11 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -170,10 +168,6 @@
abstract DockManager bindDockManager(DockManagerImpl dockManager);
@Binds
- abstract NotificationEntryManager.KeyguardEnvironment bindKeyguardEnvironment(
- KeyguardEnvironmentImpl keyguardEnvironment);
-
- @Binds
abstract ShadeController provideShadeController(ShadeControllerImpl shadeController);
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a0ecd22..318529b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -42,7 +42,6 @@
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.log.dagger.LogModule;
-import com.android.systemui.lowlightclock.LowLightClockController;
import com.android.systemui.media.dagger.MediaProjectionModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarComponent;
@@ -94,7 +93,6 @@
import com.android.systemui.wallet.dagger.WalletModule;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.dagger.DynamicOverride;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -246,21 +244,6 @@
sysuiMainExecutor));
}
- @BindsOptionalOf
- @DynamicOverride
- abstract LowLightClockController optionalLowLightClockController();
-
- @SysUISingleton
- @Provides
- static Optional<LowLightClockController> provideLowLightClockController(
- @DynamicOverride Optional<LowLightClockController> optionalController) {
- if (optionalController.isPresent() && optionalController.get().isLowLightClockEnabled()) {
- return optionalController;
- } else {
- return Optional.empty();
- }
- }
-
@Binds
abstract FgsManagerController bindFgsManagerController(FgsManagerControllerImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index c4553c4..a0f6866 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -186,6 +186,9 @@
// 801 - region sampling
public static final UnreleasedFlag REGION_SAMPLING = new UnreleasedFlag(801);
+ // 802 - wallpaper rendering
+ public static final UnreleasedFlag USE_CANVAS_RENDERER = new UnreleasedFlag(802);
+
/***************************************/
// 900 - media
public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 9ecfb75..8eca1ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -282,7 +282,8 @@
* window like any other app. This can be true while [willUnlockWithSmartspaceTransition] is
* false, if the smartspace is not available or was not ready in time.
*/
- private var willUnlockWithInWindowLauncherAnimations: Boolean = false
+ @VisibleForTesting
+ var willUnlockWithInWindowLauncherAnimations: Boolean = false
/**
* Whether we decided in [prepareForInWindowLauncherAnimations] that we are able to and want to
@@ -574,7 +575,7 @@
// Now that the Launcher surface (with its smartspace positioned identically to ours) is
// visible, hide our smartspace.
- lockscreenSmartspace!!.visibility = View.INVISIBLE
+ lockscreenSmartspace?.visibility = View.INVISIBLE
// As soon as the shade has animated out of the way, finish the keyguard exit animation. The
// in-window animations in the Launcher window will end on their own.
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockController.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockController.java
deleted file mode 100644
index 0b15f4f..0000000
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockController.java
+++ /dev/null
@@ -1,48 +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.lowlightclock;
-
-import android.view.ViewGroup;
-
-/**
- * A controller responsible for attaching and showing an optional low-light clock while dozing.
- */
-public interface LowLightClockController {
- /**
- * Returns {@code true} if the low-light clock is enabled.
- */
- boolean isLowLightClockEnabled();
-
- /**
- * Attach the low light-clock to the given parent {@link ViewGroup}.
- * @param parent The parent {@link ViewGroup} to which the low-light clock view should be
- * attached.
- */
- void attachLowLightClockView(ViewGroup parent);
-
- /**
- * Show or hide the low-light clock.
- * @param show Whether to show the low-light clock.
- * @return {@code true} if the low-light clock was shown.
- */
- boolean showLowLightClock(boolean show);
-
- /**
- * An opportunity to perform burn-in prevention.
- */
- void dozeTimeTick();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index e9caaaf..4c6aa7b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -148,6 +148,32 @@
}
}
}
+
+ companion object {
+ private const val SQUISHINESS_SCALE_START = 0.5
+ private const val SQUISHINESS_SCALE_FACTOR = 0.5
+ private fun getSquishinessScale(squishinessFraction: Float): Double {
+ return SQUISHINESS_SCALE_START + SQUISHINESS_SCALE_FACTOR * squishinessFraction
+ }
+ }
+
+ var squishinessFraction: Float = 1f
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+
+ val scale = getSquishinessScale(field)
+ for (mediaPlayer in MediaPlayerData.players()) {
+ mediaPlayer.mediaViewHolder?.let {
+ it.player.bottom = it.player.top + (scale * it.player.measuredHeight).toInt()
+ } ?: mediaPlayer.recommendationViewHolder?.let {
+ it.recommendations.bottom = it.recommendations.top +
+ (scale * it.recommendations.measuredHeight).toInt()
+ }
+ }
+ }
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
// System font changes should only happen when UMO is offscreen or a flicker may occur
@@ -1076,4 +1102,4 @@
}
fun isSsReactivated(key: String): Boolean = mediaData.get(key)?.isSsReactivated ?: false
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 53abd99..0f1ee31 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -23,35 +23,47 @@
import android.os.Bundle
import android.os.IBinder
import android.os.ResultReceiver
-import android.view.View
+import android.os.UserHandle
+import android.widget.ImageView
import com.android.internal.app.ChooserActivity
+import com.android.internal.app.ResolverListController
import com.android.internal.app.chooser.NotSelectableTargetInfo
import com.android.internal.app.chooser.TargetInfo
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.util.AsyncActivityLauncher
-import com.android.systemui.R;
-import javax.inject.Inject
+import com.android.systemui.R
+import com.android.internal.R as AndroidR
-class MediaProjectionAppSelectorActivity @Inject constructor(
- private val activityLauncher: AsyncActivityLauncher
+class MediaProjectionAppSelectorActivity constructor(
+ private val activityLauncher: AsyncActivityLauncher,
+ /** This is used to override the dependency in a screenshot test */
+ @VisibleForTesting
+ private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)? = null
) : ChooserActivity() {
+ override fun getLayoutResource() =
+ R.layout.media_projection_app_selector
+
public override fun onCreate(bundle: Bundle?) {
val queryIntent = Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
intent.putExtra(Intent.EXTRA_INTENT, queryIntent)
- // TODO(b/235465652) Use resource lexeme
- intent.putExtra(Intent.EXTRA_TITLE, "Record or cast an app")
+ // TODO(b/240939253): update copies
+ val title = getString(R.string.media_projection_dialog_service_title)
+ intent.putExtra(Intent.EXTRA_TITLE, title)
super.onCreate(bundle)
- // TODO(b/235465652) we should update VisD of the title and add an icon
- findViewById<View>(R.id.title)?.visibility = View.VISIBLE
+ requireViewById<ImageView>(AndroidR.id.icon).setImageResource(R.drawable.ic_present_to_all)
}
override fun appliedThemeResId(): Int =
R.style.Theme_SystemUI_MediaProjectionAppSelector
+ override fun createListController(userHandle: UserHandle): ResolverListController =
+ listControllerFactory?.invoke(userHandle) ?: super.createListController(userHandle)
+
override fun startSelected(which: Int, always: Boolean, filtered: Boolean) {
val currentListAdapter = mChooserMultiProfilePagerAdapter.activeListAdapter
val targetInfo = currentListAdapter.targetInfoForPosition(which, filtered) ?: return
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
index e33a1b9..9696998 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
@@ -18,8 +18,10 @@
import android.app.Activity
import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.util.AsyncActivityLauncher
import dagger.Binds
import dagger.Module
+import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@@ -29,7 +31,17 @@
@Binds
@IntoMap
@ClassKey(MediaProjectionAppSelectorActivity::class)
- abstract fun provideMediaProjectionAppSelectorActivity(
+ abstract fun bindMediaProjectionAppSelectorActivity(
activity: MediaProjectionAppSelectorActivity): Activity
+ companion object {
+ @Provides
+ fun provideMediaProjectionAppSelectorActivity(
+ activityLauncher: AsyncActivityLauncher
+ ): MediaProjectionAppSelectorActivity {
+ return MediaProjectionAppSelectorActivity(
+ activityLauncher
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 3709a86..7184fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -20,13 +20,18 @@
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.power.data.repository.PowerRepositoryModule;
import dagger.Binds;
import dagger.Module;
/** Dagger Module for code in the power package. */
-@Module
+@Module(
+ includes = {
+ PowerRepositoryModule.class,
+ }
+)
public interface PowerModule {
/** */
@Binds
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
new file mode 100644
index 0000000..b2e04bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.power.data.repository
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.PowerManager
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface for classes that act as source of truth for power-related data. */
+interface PowerRepository {
+ /** Whether the device is interactive. Starts with the current state. */
+ val isInteractive: Flow<Boolean>
+}
+
+@SysUISingleton
+class PowerRepositoryImpl
+@Inject
+constructor(
+ manager: PowerManager,
+ dispatcher: BroadcastDispatcher,
+) : PowerRepository {
+
+ override val isInteractive: Flow<Boolean> = conflatedCallbackFlow {
+ fun send() {
+ trySendWithFailureLogging(manager.isInteractive, TAG)
+ }
+
+ val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ send()
+ }
+ }
+
+ dispatcher.registerReceiver(
+ receiver,
+ IntentFilter().apply {
+ addAction(Intent.ACTION_SCREEN_ON)
+ addAction(Intent.ACTION_SCREEN_OFF)
+ },
+ )
+ send()
+
+ awaitClose { dispatcher.unregisterReceiver(receiver) }
+ }
+
+ companion object {
+ private const val TAG = "PowerRepository"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepositoryModule.kt
similarity index 61%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
copy to packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepositoryModule.kt
index e62a63a..491da65 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepositoryModule.kt
@@ -12,20 +12,15 @@
* WITHOUT WARRANTIES OR CONDITIONS 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;
+package com.android.systemui.power.data.repository
-import android.os.SystemProperties;
+import dagger.Binds
+import dagger.Module
-/**
- * Constants for desktop mode feature
- */
-public class DesktopModeConstants {
-
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+@Module
+interface PowerRepositoryModule {
+ @Binds fun bindRepository(impl: PowerRepositoryImpl): 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
new file mode 100644
index 0000000..3f799f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.power.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.power.data.repository.PowerRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Hosts business logic for interacting with the power system. */
+@SysUISingleton
+class PowerInteractor
+@Inject
+constructor(
+ repository: PowerRepository,
+) {
+ /** Whether the screen is on or off. */
+ val isInteractive: Flow<Boolean> = repository.isInteractive
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 8fe280f..b31e472 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -18,7 +18,7 @@
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.statusbar.DisableFlagsLogger.DisableState;
+import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
index 8544f61..e5d86cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
@@ -3,7 +3,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.QSFragmentDisableLog
-import com.android.systemui.statusbar.DisableFlagsLogger
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
/** A helper class for logging disable flag changes made in [QSFragment]. */
@@ -44,4 +44,4 @@
}
}
-private const val TAG = "QSFragmentDisableFlagsLog"
\ No newline at end of file
+private const val TAG = "QSFragmentDisableFlagsLog"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 8f85905..59b871c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -28,6 +28,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.media.MediaHostState;
@@ -87,13 +88,14 @@
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QS_PANEL) MediaHost mediaHost,
QSTileRevealController.Factory qsTileRevealControllerFactory,
- DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
+ DumpManager dumpManager, MediaCarouselController mediaCarouselController,
+ MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
FalsingManager falsingManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
- metricsLogger, uiEventLogger, qsLogger, dumpManager);
+ metricsLogger, uiEventLogger, qsLogger, dumpManager, mediaCarouselController);
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 6d5f844..57bea67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -31,6 +31,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -68,6 +69,7 @@
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
private final DumpManager mDumpManager;
+ private final MediaCarouselController mMediaCarouselController;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
protected boolean mShouldUseSplitNotificationShade;
@@ -122,7 +124,8 @@
MetricsLogger metricsLogger,
UiEventLogger uiEventLogger,
QSLogger qsLogger,
- DumpManager dumpManager
+ DumpManager dumpManager,
+ MediaCarouselController mediaCarouselController
) {
super(view);
mHost = host;
@@ -135,6 +138,7 @@
mDumpManager = dumpManager;
mShouldUseSplitNotificationShade =
LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+ mMediaCarouselController = mediaCarouselController;
}
@Override
@@ -152,6 +156,7 @@
public void setSquishinessFraction(float squishinessFraction) {
mView.setSquishinessFraction(squishinessFraction);
+ mMediaCarouselController.setSquishinessFraction(squishinessFraction);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index b20d7ba..67bf300 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -97,7 +97,8 @@
super(rootView);
mFooterText = mView.findViewById(R.id.footer_text);
mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon);
- mFooterIcon = new Icon.Resource(R.drawable.ic_info_outline);
+ mFooterIcon = new Icon.Resource(
+ R.drawable.ic_info_outline, /* contentDescription= */ null);
mContext = rootView.getContext();
mSecurityController = securityController;
mMainHandler = mainHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
index f632274..bd75c75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
@@ -75,6 +75,7 @@
import com.android.systemui.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.common.shared.model.ContentDescription;
import com.android.systemui.common.shared.model.Icon;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
@@ -243,16 +244,17 @@
isWorkProfileOn).toString();
Icon icon;
+ ContentDescription contentDescription = null;
if (isParentalControlsEnabled) {
- icon = new Icon.Loaded(securityModel.getDeviceAdminIcon());
+ icon = new Icon.Loaded(securityModel.getDeviceAdminIcon(), contentDescription);
} else if (vpnName != null || vpnNameWorkProfile != null) {
if (securityModel.isVpnBranded()) {
- icon = new Icon.Resource(R.drawable.stat_sys_branded_vpn);
+ icon = new Icon.Resource(R.drawable.stat_sys_branded_vpn, contentDescription);
} else {
- icon = new Icon.Resource(R.drawable.stat_sys_vpn_ic);
+ icon = new Icon.Resource(R.drawable.stat_sys_vpn_ic, contentDescription);
}
} else {
- icon = new Icon.Resource(R.drawable.ic_info_outline);
+ icon = new Icon.Resource(R.drawable.ic_info_outline, contentDescription);
}
return new SecurityButtonConfig(icon, text, isClickable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 112b1e8..46724ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -54,7 +54,9 @@
@Override
public TileLayout getOrCreateTileLayout() {
- return new QQSSideLabelTileLayout(mContext);
+ QQSSideLabelTileLayout layout = new QQSSideLabelTileLayout(mContext);
+ layout.setId(R.id.qqs_tile_layout);
+ return layout;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index be44202..c86e6e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -26,6 +26,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
@@ -63,10 +64,10 @@
@Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
Provider<Boolean> usingCollapsedLandscapeMediaProvider,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager
+ DumpManager dumpManager, MediaCarouselController mediaCarouselController
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
- uiEventLogger, qsLogger, dumpManager);
+ uiEventLogger, qsLogger, dumpManager, mediaCarouselController);
mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 2a6cf66..b585961 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -128,6 +128,8 @@
mPrivacyIconsController.setChipVisibilityListener(this);
mIconContainer.addIgnoredSlot(
getResources().getString(com.android.internal.R.string.status_bar_managed_profile));
+ mIconContainer.addIgnoredSlot(
+ getResources().getString(com.android.internal.R.string.status_bar_alarm_clock));
mIconContainer.setShouldRestrictIcons(false);
mStatusBarIconController.addIconGroup(mIconManager);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 8dd506e..28ddead 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -31,7 +31,6 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
-import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
@@ -233,10 +232,8 @@
val icon = model.icon
val iconView = button.icon
- val contentDescription = model.contentDescription
IconViewBinder.bind(icon, iconView)
- ContentDescriptionViewBinder.bind(contentDescription, iconView)
if (model.iconTint != null) {
iconView.setColorFilter(model.iconTint, PorterDuff.Mode.SRC_IN)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
index 4c0879e..2ad0513 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
@@ -18,7 +18,6 @@
import android.annotation.DrawableRes
import android.view.View
-import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
/**
@@ -29,7 +28,6 @@
val icon: Icon,
val iconTint: Int?,
@DrawableRes val background: Int,
- val contentDescription: ContentDescription,
// TODO(b/230830644): Replace View by an Expandable interface that can expand in either dialog
// or activity.
val onClick: (View) -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index b556a3e..a935338 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -138,10 +138,12 @@
/** The model for the settings button. */
val settings: FooterActionsButtonViewModel =
FooterActionsButtonViewModel(
- Icon.Resource(R.drawable.ic_settings),
+ Icon.Resource(
+ R.drawable.ic_settings,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ ),
iconTint = null,
R.drawable.qs_footer_action_circle,
- ContentDescription.Resource(R.string.accessibility_quick_settings_settings),
this::onSettingsButtonClicked,
)
@@ -149,14 +151,16 @@
val power: FooterActionsButtonViewModel? =
if (showPowerButton) {
FooterActionsButtonViewModel(
- Icon.Resource(android.R.drawable.ic_lock_power_off),
+ Icon.Resource(
+ android.R.drawable.ic_lock_power_off,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ ),
iconTint =
Utils.getColorAttrDefaultColor(
context,
com.android.internal.R.attr.textColorOnAccent,
),
R.drawable.qs_footer_action_circle_color,
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu),
this::onPowerButtonClicked,
)
} else {
@@ -252,10 +256,12 @@
}
return FooterActionsButtonViewModel(
- Icon.Loaded(icon),
+ Icon.Loaded(
+ icon,
+ ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
+ ),
iconTint,
R.drawable.qs_footer_action_circle,
- ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
this::onUserSwitcherClicked,
)
}
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 be6982a..5842665 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -165,7 +165,7 @@
iv.clearColorFilter();
}
if (state.state != mState) {
- int color = getColor(state.state);
+ int color = getColor(state);
mState = state.state;
if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
@@ -183,7 +183,7 @@
}
}
- protected int getColor(int state) {
+ protected int getColor(QSTile.State state) {
return getIconColorForState(getContext(), state);
}
@@ -239,19 +239,18 @@
/**
* Color to tint the tile icon based on state
*/
- public static int getIconColorForState(Context context, int state) {
- switch (state) {
- case Tile.STATE_UNAVAILABLE:
- return Utils.applyAlpha(QSTileViewImpl.UNAVAILABLE_ALPHA,
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary));
- case Tile.STATE_INACTIVE:
- return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
- case Tile.STATE_ACTIVE:
- return Utils.getColorAttrDefaultColor(context,
- com.android.internal.R.attr.textColorOnAccent);
- default:
- Log.e("QSIconView", "Invalid state " + state);
- return 0;
+ private static int getIconColorForState(Context context, QSTile.State state) {
+ if (state.disabledByPolicy || state.state == Tile.STATE_UNAVAILABLE) {
+ return Utils.applyAlpha(QSTileViewImpl.UNAVAILABLE_ALPHA,
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary));
+ } else if (state.state == Tile.STATE_INACTIVE) {
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
+ } else if (state.state == Tile.STATE_ACTIVE) {
+ return Utils.getColorAttrDefaultColor(context,
+ com.android.internal.R.attr.textColorOnAccent);
+ } else {
+ Log.e("QSIconView", "Invalid state " + state);
+ return 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 2731d64..163ee2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -35,6 +35,7 @@
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Switch
@@ -144,6 +145,7 @@
superSetVisibility = { super.setVisibility(it) },
superSetTransitionVisibility = { super.setTransitionVisibility(it) },
)
+ private var lastDisabledByPolicy = false
private val locInScreen = IntArray(2)
@@ -376,8 +378,22 @@
super.onInitializeAccessibilityNodeInfo(info)
// Clear selected state so it is not announce by talkback.
info.isSelected = false
+ if (lastDisabledByPolicy) {
+ info.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id,
+ resources.getString(
+ R.string.accessibility_tile_disabled_by_policy_action_description
+ )
+ )
+ )
+ }
if (!TextUtils.isEmpty(accessibilityClass)) {
- info.className = accessibilityClass
+ info.className = if (lastDisabledByPolicy) {
+ Button::class.java.name
+ } else {
+ accessibilityClass
+ }
if (Switch::class.java.name == accessibilityClass) {
val label = resources.getString(
if (tileState) R.string.switch_bar_on else R.string.switch_bar_off)
@@ -430,6 +446,10 @@
state.secondaryLabel = stateText
}
}
+ if (state.disabledByPolicy && state.state != Tile.STATE_UNAVAILABLE) {
+ stateDescription.append(", ")
+ stateDescription.append(getUnavailableText(state.spec))
+ }
if (!TextUtils.isEmpty(state.stateDescription)) {
stateDescription.append(", ")
stateDescription.append(state.stateDescription)
@@ -470,38 +490,38 @@
}
// Colors
- if (state.state != lastState) {
+ if (state.state != lastState || state.disabledByPolicy || lastDisabledByPolicy) {
singleAnimator.cancel()
if (allowAnimations) {
singleAnimator.setValues(
colorValuesHolder(
BACKGROUND_NAME,
paintColor,
- getBackgroundColorForState(state.state)
+ getBackgroundColorForState(state.state, state.disabledByPolicy)
),
colorValuesHolder(
LABEL_NAME,
label.currentTextColor,
- getLabelColorForState(state.state)
+ getLabelColorForState(state.state, state.disabledByPolicy)
),
colorValuesHolder(
SECONDARY_LABEL_NAME,
secondaryLabel.currentTextColor,
- getSecondaryLabelColorForState(state.state)
+ getSecondaryLabelColorForState(state.state, state.disabledByPolicy)
),
colorValuesHolder(
CHEVRON_NAME,
chevronView.imageTintList?.defaultColor ?: 0,
- getChevronColorForState(state.state)
+ getChevronColorForState(state.state, state.disabledByPolicy)
)
)
singleAnimator.start()
} else {
setAllColors(
- getBackgroundColorForState(state.state),
- getLabelColorForState(state.state),
- getSecondaryLabelColorForState(state.state),
- getChevronColorForState(state.state)
+ getBackgroundColorForState(state.state, state.disabledByPolicy),
+ getLabelColorForState(state.state, state.disabledByPolicy),
+ getSecondaryLabelColorForState(state.state, state.disabledByPolicy),
+ getChevronColorForState(state.state, state.disabledByPolicy)
)
}
}
@@ -512,6 +532,7 @@
label.isEnabled = !state.disabledByPolicy
lastState = state.state
+ lastDisabledByPolicy = state.disabledByPolicy
}
private fun setAllColors(
@@ -559,13 +580,14 @@
}
}
- private fun getStateText(state: QSTile.State): String {
- if (state.disabledByPolicy) {
- return context.getString(R.string.tile_disabled)
- }
+ private fun getUnavailableText(spec: String?): String {
+ val arrayResId = SubtitleArrayMapping.getSubtitleId(spec)
+ return resources.getStringArray(arrayResId)[Tile.STATE_UNAVAILABLE]
+ }
+ private fun getStateText(state: QSTile.State): String {
return if (state.state == Tile.STATE_UNAVAILABLE || state is BooleanState) {
- var arrayResId = SubtitleArrayMapping.getSubtitleId(state.spec)
+ val arrayResId = SubtitleArrayMapping.getSubtitleId(state.spec)
val array = resources.getStringArray(arrayResId)
array[state.state]
} else {
@@ -587,11 +609,11 @@
return locInScreen.get(1) >= -height
}
- private fun getBackgroundColorForState(state: Int): Int {
- return when (state) {
- Tile.STATE_ACTIVE -> colorActive
- Tile.STATE_INACTIVE -> colorInactive
- Tile.STATE_UNAVAILABLE -> colorUnavailable
+ private fun getBackgroundColorForState(state: Int, disabledByPolicy: Boolean = false): Int {
+ return when {
+ state == Tile.STATE_UNAVAILABLE || disabledByPolicy -> colorUnavailable
+ state == Tile.STATE_ACTIVE -> colorActive
+ state == Tile.STATE_INACTIVE -> colorInactive
else -> {
Log.e(TAG, "Invalid state $state")
0
@@ -599,11 +621,11 @@
}
}
- private fun getLabelColorForState(state: Int): Int {
- return when (state) {
- Tile.STATE_ACTIVE -> colorLabelActive
- Tile.STATE_INACTIVE -> colorLabelInactive
- Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
+ private fun getLabelColorForState(state: Int, disabledByPolicy: Boolean = false): Int {
+ return when {
+ state == Tile.STATE_UNAVAILABLE || disabledByPolicy -> colorLabelUnavailable
+ state == Tile.STATE_ACTIVE -> colorLabelActive
+ state == Tile.STATE_INACTIVE -> colorLabelInactive
else -> {
Log.e(TAG, "Invalid state $state")
0
@@ -611,11 +633,11 @@
}
}
- private fun getSecondaryLabelColorForState(state: Int): Int {
- return when (state) {
- Tile.STATE_ACTIVE -> colorSecondaryLabelActive
- Tile.STATE_INACTIVE -> colorSecondaryLabelInactive
- Tile.STATE_UNAVAILABLE -> colorSecondaryLabelUnavailable
+ private fun getSecondaryLabelColorForState(state: Int, disabledByPolicy: Boolean = false): Int {
+ return when {
+ state == Tile.STATE_UNAVAILABLE || disabledByPolicy -> colorSecondaryLabelUnavailable
+ state == Tile.STATE_ACTIVE -> colorSecondaryLabelActive
+ state == Tile.STATE_INACTIVE -> colorSecondaryLabelInactive
else -> {
Log.e(TAG, "Invalid state $state")
0
@@ -623,7 +645,16 @@
}
}
- private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state)
+ private fun getChevronColorForState(state: Int, disabledByPolicy: Boolean = false): Int =
+ getSecondaryLabelColorForState(state, disabledByPolicy)
+
+ @VisibleForTesting
+ internal fun getCurrentColors(): List<Int> = listOf(
+ paintColor,
+ label.currentTextColor,
+ secondaryLabel.currentTextColor,
+ chevronView.imageTintList?.defaultColor ?: 0
+ )
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 05b3420..c65bd9b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -31,7 +31,6 @@
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -42,7 +41,6 @@
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
@@ -55,8 +53,7 @@
statusBarStateController: StatusBarStateController,
activityStarter: ActivityStarter,
qsLogger: QSLogger,
- private val controlsComponent: ControlsComponent,
- private val keyguardStateController: KeyguardStateController
+ private val controlsComponent: ControlsComponent
) : QSTileImpl<QSTile.State>(
host,
backgroundLooper,
@@ -105,7 +102,8 @@
}
val intent = Intent().apply {
- component = ComponentName(mContext, ControlsActivity::class.java)
+ component = ComponentName(mContext, controlsComponent.getControlsUiController().get()
+ .resolveActivity())
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(ControlsUiController.EXTRA_ANIMATE, true)
}
@@ -127,10 +125,15 @@
state.icon = icon
if (controlsComponent.isEnabled() && hasControlsApps.get()) {
if (controlsComponent.getVisibility() == AVAILABLE) {
- val structure = controlsComponent
- .getControlsController().get().getPreferredStructure().structure
- state.state = Tile.STATE_ACTIVE
- state.secondaryLabel = if (structure == tileLabel) null else structure
+ val structureInfo = controlsComponent
+ .getControlsController().get().getPreferredStructure()
+ state.state = if (structureInfo.controls.isEmpty()) {
+ Tile.STATE_INACTIVE
+ } else {
+ Tile.STATE_ACTIVE
+ }
+ val label = structureInfo.structure
+ state.secondaryLabel = if (label == tileLabel) null else label
} else {
state.state = Tile.STATE_INACTIVE
state.secondaryLabel = mContext.getText(R.string.controls_tile_locked)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index fab70fc..6b32daf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -270,6 +270,14 @@
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(qsCarrierGroup)
.build()
+
+ if (!combinedHeaders) {
+ // In the new header, we display alarm icon but we ignore it when not using the new
+ // headers.
+ iconContainer.addIgnoredSlot(
+ context.getString(com.android.internal.R.string.status_bar_alarm_clock)
+ )
+ }
}
override fun onViewAttached() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 70c0cab..91c6a9c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -97,6 +97,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.ActiveUnlockConfig;
+import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
@@ -161,7 +162,6 @@
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
@@ -518,7 +518,6 @@
}
}).setCustomInterpolator(
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
- private final NotificationEntryManager mEntryManager;
private final CommandQueue mCommandQueue;
private final UserManager mUserManager;
@@ -709,7 +708,6 @@
DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController, FalsingManager falsingManager,
FalsingCollector falsingCollector,
- NotificationEntryManager notificationEntryManager,
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
StatusBarWindowStateController statusBarWindowStateController,
@@ -867,7 +865,6 @@
});
mBottomAreaShadeAlphaAnimator.setDuration(160);
mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
- mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager;
mAuthController = authController;
mLockIconViewController = lockIconViewController;
@@ -2355,7 +2352,7 @@
// When expanding QS, let's authenticate the user if possible,
// this will speed up notification actions.
if (height == 0) {
- mCentralSurfaces.requestFaceAuth(false);
+ mCentralSurfaces.requestFaceAuth(false, FaceAuthApiRequestReason.QS_EXPANDED);
}
}
@@ -3532,7 +3529,8 @@
&& !mUpdateMonitor.isFaceDetectionRunning()
&& !mUpdateMonitor.getUserCanSkipBouncer(
KeyguardUpdateMonitor.getCurrentUser())) {
- mUpdateMonitor.requestFaceAuth(true);
+ mUpdateMonitor.requestFaceAuth(true,
+ FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
} else {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
@@ -4729,7 +4727,6 @@
public void showAodUi() {
setDozing(true /* dozing */, false /* animate */);
mStatusBarStateController.setUpcomingState(KEYGUARD);
- mEntryManager.updateNotifications("showAodUi");
mStatusBarStateListener.onStateChanged(KEYGUARD);
mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
setExpandedFraction(1f);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index fec76b6..e93f605 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -34,7 +34,6 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.lowlightclock.LowLightClockController;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -51,7 +50,6 @@
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import java.io.PrintWriter;
-import java.util.Optional;
import javax.inject.Inject;
@@ -88,7 +86,6 @@
private final DockManager mDockManager;
private final NotificationPanelViewController mNotificationPanelViewController;
private final PanelExpansionStateManager mPanelExpansionStateManager;
- private final Optional<LowLightClockController> mLowLightClockController;
private boolean mIsTrackingBarGesture = false;
@@ -106,7 +103,6 @@
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
StatusBarWindowStateController statusBarWindowStateController,
LockIconViewController lockIconViewController,
- Optional<LowLightClockController> lowLightClockController,
CentralSurfaces centralSurfaces,
NotificationShadeWindowController controller,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -125,7 +121,6 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mStatusBarWindowStateController = statusBarWindowStateController;
mLockIconViewController = lockIconViewController;
- mLowLightClockController = lowLightClockController;
mService = centralSurfaces;
mNotificationShadeWindowController = controller;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
@@ -148,8 +143,6 @@
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
mGestureDetector = new GestureDetector(mView.getContext(), mPulsingGestureListener);
- mLowLightClockController.ifPresent(controller -> controller.attachLowLightClockView(mView));
-
mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
@Override
public Boolean handleDispatchTouchEvent(MotionEvent ev) {
@@ -434,21 +427,6 @@
mStatusBarViewController = statusBarViewController;
}
- /**
- * Tell the controller that dozing has begun or ended.
- * @param dozing True if dozing has begun.
- */
- public void setDozing(boolean dozing) {
- mLowLightClockController.ifPresent(controller -> controller.showLowLightClock(dozing));
- }
-
- /**
- * Tell the controller to perform burn-in prevention.
- */
- public void dozeTimeTick() {
- mLowLightClockController.ifPresent(LowLightClockController::dozeTimeTick);
- }
-
@VisibleForTesting
void setDragDownHelper(DragDownHelper dragDownHelper) {
mDragDownHelper = dragDownHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index 11e36c9..a0076937 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -75,7 +75,7 @@
import java.io.PrintWriter;
import java.util.List;
-abstract class PanelViewController {
+public abstract class PanelViewController {
public static final String TAG = NotificationPanelView.class.getSimpleName();
public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
public static final float FLING_SPEED_UP_FACTOR = 0.6f;
@@ -397,7 +397,7 @@
mInitialOffsetOnTouch = expandedHeight;
mInitialExpandY = newY;
mInitialExpandX = newX;
- mInitialTouchFromKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+ mInitialTouchFromKeyguard = mKeyguardStateController.isShowing();
if (startTracking) {
mTouchSlopExceeded = true;
setExpandedHeight(mInitialOffsetOnTouch);
@@ -416,9 +416,7 @@
float vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
- final boolean onKeyguard =
- mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
-
+ final boolean onKeyguard = mKeyguardStateController.isShowing();
final boolean expand;
if (mKeyguardStateController.isKeyguardFadingAway()
|| (mInitialTouchFromKeyguard && !onKeyguard)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index ee79a4d..9d4a27c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -50,7 +50,6 @@
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetsType;
@@ -74,7 +73,6 @@
import com.android.systemui.tracing.ProtoTracer;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -150,7 +148,6 @@
private static final int MSG_TRACING_STATE_CHANGED = 54 << MSG_SHIFT;
private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 55 << MSG_SHIFT;
private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 56 << MSG_SHIFT;
- private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 57 << MSG_SHIFT;
//TODO(b/169175022) Update name and when feature name is locked.
private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 58 << MSG_SHIFT;
private static final int MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED = 59 << MSG_SHIFT;
@@ -423,11 +420,6 @@
default void requestWindowMagnificationConnection(boolean connect) { }
/**
- * Handles a window manager shell logging command.
- */
- default void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {}
-
- /**
* @see IStatusBar#setNavigationBarLumaSamplingEnabled(int, boolean)
*/
default void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {}
@@ -1140,17 +1132,6 @@
}
@Override
- public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {
- synchronized (mLock) {
- SomeArgs internalArgs = SomeArgs.obtain();
- internalArgs.arg1 = args;
- internalArgs.arg2 = outFd;
- mHandler.obtainMessage(MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND, internalArgs)
- .sendToTarget();
- }
- }
-
- @Override
public void suppressAmbientDisplay(boolean suppress) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_SUPPRESS_AMBIENT_DISPLAY, suppress).sendToTarget();
@@ -1634,18 +1615,6 @@
mCallbacks.get(i).requestWindowMagnificationConnection((Boolean) msg.obj);
}
break;
- case MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND:
- args = (SomeArgs) msg.obj;
- try (ParcelFileDescriptor pfd = (ParcelFileDescriptor) args.arg2) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).handleWindowManagerLoggingCommand(
- (String[]) args.arg1, pfd);
- }
- } catch (IOException e) {
- Log.e(TAG, "Failed to handle logging command", e);
- }
- args.recycle();
- break;
case MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setNavigationBarLumaSamplingEnabled(msg.arg1,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 47dc5c2..2251ab5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,6 +19,10 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_FIRST_FRAME_RECEIVED;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -78,6 +82,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
+import com.android.systemui.biometrics.BiometricMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -178,7 +183,7 @@
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
private String mBiometricErrorMessageToShowOnScreenOn;
- private final Set<Integer> mCoExFaceHelpMsgIdsToShow;
+ private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
private boolean mInited;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -249,11 +254,11 @@
mScreenLifecycle = screenLifecycle;
mScreenLifecycle.addObserver(mScreenObserver);
- mCoExFaceHelpMsgIdsToShow = new HashSet<>();
+ mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
int[] msgIds = context.getResources().getIntArray(
com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled);
for (int msgId : msgIds) {
- mCoExFaceHelpMsgIdsToShow.add(msgId);
+ mCoExFaceAcquisitionMsgIdsToShow.add(msgId);
}
mHandler = new Handler(mainLooper) {
@@ -990,7 +995,7 @@
mTopIndicationView == null ? null : mTopIndicationView.getText()));
pw.println(" computePowerIndication(): " + computePowerIndication());
pw.println(" trustGrantedIndication: " + getTrustGrantedIndication());
- pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceHelpMsgIdsToShow);
+ pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceAcquisitionMsgIdsToShow);
mRotateTextViewController.dump(pw, args);
}
@@ -1048,6 +1053,17 @@
return;
}
+ if (biometricSourceType == FACE) {
+ if (msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED) {
+ mFaceAcquiredMessageDeferral.reset();
+ } else {
+ mFaceAcquiredMessageDeferral.processMessage(msgId, helpString);
+ if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) {
+ return;
+ }
+ }
+ }
+
final boolean faceAuthSoftError = biometricSourceType == FACE
&& msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
final boolean faceAuthFailed = biometricSourceType == FACE
@@ -1055,9 +1071,9 @@
final boolean isUnlockWithFingerprintPossible =
mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
getCurrentUser());
- if (faceAuthSoftError
- && isUnlockWithFingerprintPossible
- && !mCoExFaceHelpMsgIdsToShow.contains(msgId)) {
+ final boolean isCoExFaceAcquisitionMessage =
+ faceAuthSoftError && isUnlockWithFingerprintPossible;
+ if (isCoExFaceAcquisitionMessage && !mCoExFaceAcquisitionMsgIdsToShow.contains(msgId)) {
if (DEBUG) {
Log.d(TAG, "skip showing msgId=" + msgId + " helpString=" + helpString
+ ", due to co-ex logic");
@@ -1067,7 +1083,12 @@
mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- if (faceAuthFailed && isUnlockWithFingerprintPossible) {
+ if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
+ showBiometricMessage(
+ helpString,
+ mContext.getString(R.string.keyguard_suggest_fingerprint)
+ );
+ } else if (faceAuthFailed && isUnlockWithFingerprintPossible) {
showBiometricMessage(
mContext.getString(R.string.keyguard_face_failed),
mContext.getString(R.string.keyguard_suggest_fingerprint)
@@ -1090,34 +1111,44 @@
@Override
public void onBiometricError(int msgId, String errString,
BiometricSourceType biometricSourceType) {
- if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
- return;
+ CharSequence deferredFaceMessage = null;
+ if (biometricSourceType == FACE) {
+ deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ mFaceAcquiredMessageDeferral.reset();
}
- if (biometricSourceType == FACE
- && msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) {
- // suppress all face UNABLE_TO_PROCESS errors
+ if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
if (DEBUG) {
- Log.d(TAG, "skip showing FACE_ERROR_UNABLE_TO_PROCESS errString="
- + errString);
+ Log.d(TAG, "suppressingBiometricError msgId=" + msgId
+ + " source=" + biometricSourceType);
}
- } else if (biometricSourceType == FACE
- && msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ } else if (biometricSourceType == FACE && msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ // Co-ex: show deferred message OR nothing
if (mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- getCurrentUser())) {
- // no message if fingerprint is also enrolled
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ // if we're on the lock screen (bouncer isn't showing), show the deferred msg
+ if (deferredFaceMessage != null
+ && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_suggest_fingerprint)
+ );
+ return;
+ }
+
+ // otherwise, don't show any message
if (DEBUG) {
Log.d(TAG, "skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
}
return;
}
- // The face timeout message is not very actionable, let's ask the user to
+ // Face-only: The face timeout message is not very actionable, let's ask the user to
// manually retry.
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(
- mContext.getResources().getString(R.string.keyguard_try_fingerprint),
- mInitialTextColorState
+ if (deferredFaceMessage != null) {
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_unlock)
);
} else {
// suggest swiping up to unlock (try face auth again or swipe up to bouncer)
@@ -1134,8 +1165,9 @@
private boolean shouldSuppressBiometricError(int msgId,
BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
- if (biometricSourceType == BiometricSourceType.FINGERPRINT)
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
return shouldSuppressFingerprintError(msgId, updateMonitor);
+ }
if (biometricSourceType == FACE) {
return shouldSuppressFaceError(msgId, updateMonitor);
}
@@ -1161,7 +1193,8 @@
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
- || msgId == FaceManager.FACE_ERROR_CANCELED);
+ || msgId == FaceManager.FACE_ERROR_CANCELED
+ || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS);
}
@@ -1200,10 +1233,11 @@
boolean isStrongBiometric) {
super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric);
hideBiometricMessage();
-
- if (biometricSourceType == FACE
- && !mKeyguardBypassController.canBypass()) {
- showActionToUnlock();
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.reset();
+ if (!mKeyguardBypassController.canBypass()) {
+ showActionToUnlock();
+ }
}
}
@@ -1274,4 +1308,14 @@
}
}
};
+
+ private final BiometricMessageDeferral mFaceAcquiredMessageDeferral =
+ new BiometricMessageDeferral(
+ Set.of(
+ FACE_ACQUIRED_GOOD,
+ FACE_ACQUIRED_START,
+ FACE_ACQUIRED_FIRST_FRAME_RECEIVED
+ ),
+ Set.of(FACE_ACQUIRED_TOO_DARK)
+ );
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 74d8f1b..d3343df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -2,7 +2,6 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
-import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
@@ -880,15 +879,22 @@
child.actualHeight = (child.collapsedHeight + rubberband).toInt()
}
- private fun cancelChildExpansion(child: ExpandableView) {
+ @VisibleForTesting
+ fun cancelChildExpansion(
+ child: ExpandableView,
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
+ ) {
if (child.actualHeight == child.collapsedHeight) {
expandCallback.setUserLockedChild(child, false)
return
}
- val anim = ObjectAnimator.ofInt(child, "actualHeight",
- child.actualHeight, child.collapsedHeight)
+ val anim = ValueAnimator.ofInt(child.actualHeight, child.collapsedHeight)
anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
- anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS
+ anim.duration = animationDuration
+ anim.addUpdateListener { animation: ValueAnimator ->
+ // don't use reflection, because the `actualHeight` field may be obfuscated
+ child.actualHeight = animation.animatedValue as Int
+ }
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
expandCallback.setUserLockedChild(child, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index ae5a2c3..8699441 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -51,7 +51,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -85,9 +84,6 @@
private final SecureSettings mSecureSettings;
private final Object mLock = new Object();
- // Lazy
- private NotificationEntryManager mEntryManager;
-
private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy;
private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy;
private final DevicePolicyManager mDevicePolicyManager;
@@ -119,7 +115,6 @@
isCurrentProfile(getSendingUserId())) {
mUsersAllowingPrivateNotifications.clear();
updateLockscreenNotificationSetting();
- getEntryManager().updateNotifications("ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED");
// TODO(b/231976036): Consolidate pipeline invalidations related to this event
// notifyNotificationStateChanged();
}
@@ -140,10 +135,6 @@
updateLockscreenNotificationSetting();
updatePublicMode();
- // The filtering needs to happen before the update call below in order to
- // make sure
- // the presenter has the updated notifications from the new user
- getEntryManager().reapplyFilterAndSort("user switched");
mPresenter.onUserSwitched(mCurrentUserId);
for (UserChangedListener listener : mListeners) {
@@ -200,13 +191,6 @@
protected ContentObserver mSettingsObserver;
private boolean mHideSilentNotificationsOnLockscreen;
- private NotificationEntryManager getEntryManager() {
- if (mEntryManager == null) {
- mEntryManager = Dependency.get(NotificationEntryManager.class);
- }
- return mEntryManager;
- }
-
@Inject
public NotificationLockscreenUserManagerImpl(Context context,
BroadcastDispatcher broadcastDispatcher,
@@ -253,8 +237,6 @@
mUsersAllowingNotifications.clear();
// ... and refresh all the notifications
updateLockscreenNotificationSetting();
- getEntryManager().updateNotifications("LOCK_SCREEN_SHOW_NOTIFICATIONS,"
- + " or LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS change");
notifyNotificationStateChanged();
}
};
@@ -264,8 +246,6 @@
public void onChange(boolean selfChange) {
updateLockscreenNotificationSetting();
if (mDeviceProvisionedController.isDeviceProvisioned()) {
- getEntryManager().updateNotifications("LOCK_SCREEN_ALLOW_REMOTE_INPUT"
- + " or ZEN_MODE change");
// TODO(b/231976036): Consolidate pipeline invalidations related to this event
// notifyNotificationStateChanged();
}
@@ -596,7 +576,6 @@
setLockscreenPublicMode(isProfilePublic, userId);
mUsersWithSeparateWorkChallenge.put(userId, needsSeparateChallenge);
}
- getEntryManager().updateNotifications("NotificationLockscreenUserManager.updatePublicMode");
// TODO(b/234738798): Migrate KeyguardNotificationVisibilityProvider to use this listener
if (!mLockscreenPublicMode.equals(oldPublicModes)
|| !mUsersWithSeparateWorkChallenge.equals(oldWorkChallenges)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index d74d408..f668528 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -53,8 +53,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -256,7 +254,6 @@
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager notificationEntryManager,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
RemoteInputUriController remoteInputUriController,
@@ -279,25 +276,6 @@
mClickNotifier = clickNotifier;
dumpManager.registerDumpable(this);
-
- notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- // Mark smart replies as sent whenever a notification is updated - otherwise the
- // smart replies are never marked as sent.
- mSmartReplyController.stopSending(entry);
- }
-
- @Override
- public void onEntryRemoved(
- @Nullable NotificationEntry entry,
- NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- // We're removing the notification, the smart controller can forget about it.
- mSmartReplyController.stopSending(entry);
- }
- });
}
/** Add a listener for various remote input events. Works with NEW pipeline only. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 6302ef1..bbff046 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -18,7 +18,7 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
-import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
import android.os.PowerManager
@@ -28,6 +28,7 @@
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.ViewConfiguration
+import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
@@ -276,15 +277,22 @@
}
}
- private fun reset(child: ExpandableView) {
+ @VisibleForTesting
+ fun reset(
+ child: ExpandableView,
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
+ ) {
if (child.actualHeight == child.collapsedHeight) {
setUserLocked(child, false)
return
}
- val anim = ObjectAnimator.ofInt(child, "actualHeight",
- child.actualHeight, child.collapsedHeight)
+ val anim = ValueAnimator.ofInt(child.actualHeight, child.collapsedHeight)
anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
- anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
+ anim.duration = animationDuration
+ anim.addUpdateListener { animation: ValueAnimator ->
+ // don't use reflection, because the `actualHeight` field may be obfuscated
+ child.actualHeight = animation.animatedValue as Int
+ }
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
setUserLocked(child, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index a57d849b2..25c6dce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -38,6 +38,7 @@
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import java.util.ArrayList;
@@ -64,6 +65,15 @@
/**
* Designated constructor
+ *
+ * This view is special, in that it is the only view in SystemUI that allows for a configuration
+ * override on a MCC/MNC-basis. This means that for every mobile view inflated, we have to
+ * construct a context with that override, since the resource system doesn't have a way to
+ * handle this for us.
+ *
+ * @param context A context with resources configured by MCC/MNC
+ * @param slot The string key defining which slot this icon refers to. Always "mobile" for the
+ * mobile icon
*/
public static StatusBarMobileView fromContext(
Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index c74621d..867be1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.AudioAttributes;
+import android.os.Process;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -33,6 +34,7 @@
import javax.inject.Inject;
/**
+ *
*/
@SysUISingleton
public class VibratorHelper {
@@ -40,9 +42,18 @@
private final Vibrator mVibrator;
private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+
+ private static final VibrationEffect BIOMETRIC_SUCCESS_VIBRATION_EFFECT =
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ private static final VibrationEffect BIOMETRIC_ERROR_VIBRATION_EFFECT =
+ VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+
private final Executor mExecutor;
/**
+ *
*/
@Inject
public VibratorHelper(@Nullable Vibrator vibrator, @Background Executor executor) {
@@ -109,4 +120,23 @@
}
mExecutor.execute(mVibrator::cancel);
}
+
+ /**
+ * Perform vibration when biometric authentication success
+ */
+ public void vibrateAuthSuccess(String reason) {
+ vibrate(Process.myUid(),
+ "com.android.systemui",
+ BIOMETRIC_SUCCESS_VIBRATION_EFFECT, reason,
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ }
+
+ /**
+ * Perform vibration when biometric authentication error
+ */
+ public void vibrateAuthError(String reason) {
+ vibrate(Process.myUid(), "com.android.systemui",
+ BIOMETRIC_ERROR_VIBRATION_EFFECT, reason,
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt
new file mode 100644
index 0000000..a02dd34
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.connectivity.ui
+
+import android.content.Context
+import android.content.res.Configuration
+import android.os.Bundle
+import android.telephony.SubscriptionInfo
+import android.view.ContextThemeWrapper
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.demomode.DemoMode
+import com.android.systemui.demomode.DemoMode.COMMAND_NETWORK
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.connectivity.NetworkController
+import com.android.systemui.statusbar.connectivity.SignalCallback
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Every subscriptionId can have its own CarrierConfig associated with it, so we have to create our
+ * own [Configuration] and track resources based on the full set of available mcc-mnc combinations.
+ *
+ * (for future reference: b/240555502 is the initiating bug for this)
+ */
+@SysUISingleton
+class MobileContextProvider
+@Inject
+constructor(
+ networkController: NetworkController,
+ dumpManager: DumpManager,
+ private val demoModeController: DemoModeController,
+) : Dumpable, DemoMode {
+ private val subscriptions = mutableMapOf<Int, SubscriptionInfo>()
+ private val signalCallback =
+ object : SignalCallback {
+ override fun setSubs(subs: List<SubscriptionInfo>) {
+ subscriptions.clear()
+ subs.forEach { info -> subscriptions[info.subscriptionId] = info }
+ }
+ }
+
+ // These should always be null when not in demo mode
+ private var demoMcc: Int? = null
+ private var demoMnc: Int? = null
+
+ init {
+ networkController.addCallback(signalCallback)
+ dumpManager.registerDumpable(this)
+ demoModeController.addCallback(this)
+ }
+
+ /**
+ * @return a context with the MCC/MNC [Configuration] values corresponding to this
+ * subscriptionId
+ */
+ fun getMobileContextForSub(subId: Int, context: Context): Context {
+ if (demoModeController.isInDemoMode) {
+ return createMobileContextForDemoMode(context)
+ }
+
+ // Fail back to the given context if no sub exists
+ val info = subscriptions[subId] ?: return context
+
+ return createCarrierConfigContext(context, info.mcc, info.mnc)
+ }
+
+ /** For Demo mode (for now), just apply the same MCC/MNC override for all subIds */
+ private fun createMobileContextForDemoMode(context: Context): Context {
+ return createCarrierConfigContext(context, demoMcc ?: 0, demoMnc ?: 0)
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println(
+ "Subscriptions below will be inflated with a configuration context with " +
+ "MCC/MNC overrides"
+ )
+ subscriptions.forEach { (subId, info) ->
+ pw.println(" Subscription with subId($subId) with MCC/MNC(${info.mcc}/${info.mnc})")
+ }
+ pw.println(" MCC override: ${demoMcc ?: "(none)"}")
+ pw.println(" MNC override: ${demoMnc ?: "(none)"}")
+ }
+
+ override fun demoCommands(): List<String> {
+ return listOf(COMMAND_NETWORK)
+ }
+
+ override fun onDemoModeFinished() {
+ demoMcc = null
+ demoMnc = null
+ }
+
+ override fun dispatchDemoCommand(command: String, args: Bundle) {
+ val mccmnc = args.getString("mccmnc") ?: return
+ // Only length 5/6 strings are valid
+ if (!(mccmnc.length == 5 || mccmnc.length == 6)) {
+ return
+ }
+
+ // MCC is always the first 3 digits, and mnc is the last 2 or 3
+ demoMcc = mccmnc.subSequence(0, 3).toString().toInt()
+ demoMnc = mccmnc.subSequence(3, mccmnc.length).toString().toInt()
+ }
+
+ companion object {
+ /**
+ * Creates a context based on this [SubscriptionInfo]'s MCC/MNC values, allowing the overlay
+ * system to properly load different carrier's iconography
+ */
+ private fun createCarrierConfigContext(context: Context, mcc: Int, mnc: Int): Context {
+ // Copy the existing configuration
+ val c = Configuration(context.resources.configuration)
+ c.mcc = mcc
+ c.mnc = mnc
+
+ return ContextThemeWrapper(context, context.theme).also { ctx ->
+ ctx.applyOverrideConfiguration(c)
+ }
+ }
+ }
+}
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 8752f92..7cd79ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -18,7 +18,6 @@
import android.app.IActivityManager;
import android.content.Context;
-import android.os.Handler;
import android.os.RemoteException;
import android.service.dreams.IDreamManager;
import android.util.Log;
@@ -42,14 +41,12 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.RemoteInputNotificationRebuilder;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -99,11 +96,8 @@
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager notificationEntryManager,
- RemoteInputNotificationRebuilder rebuilder,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
- Handler mainHandler,
RemoteInputUriController remoteInputUriController,
NotificationClickNotifier clickNotifier,
ActionClickLogger actionClickLogger,
@@ -114,7 +108,6 @@
lockscreenUserManager,
smartReplyController,
visibilityProvider,
- notificationEntryManager,
centralSurfacesOptionalLazy,
statusBarStateController,
remoteInputUriController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt
index 66591b3..f04159c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt
@@ -13,23 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar
+package com.android.systemui.statusbar.disableflags
+import android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS
+import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
+import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
+import android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS
+import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
import android.app.StatusBarManager.DISABLE_BACK
import android.app.StatusBarManager.DISABLE_CLOCK
import android.app.StatusBarManager.DISABLE_EXPAND
import android.app.StatusBarManager.DISABLE_HOME
-import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP
import android.app.StatusBarManager.DISABLE_RECENT
import android.app.StatusBarManager.DISABLE_SEARCH
import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
-import android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS
-import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
-import android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS
-import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
-import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
@@ -213,4 +213,4 @@
DisableFlagsLogger.DisableFlag(DISABLE2_GLOBAL_ACTIONS, 'G', 'g'),
DisableFlagsLogger.DisableFlag(DISABLE2_ROTATE_SUGGESTIONS, 'R', 'r')
)
-// LINT.ThenChange(frameworks/base/core/java/android/app/StatusBarManager.java)
\ No newline at end of file
+// LINT.ThenChange(frameworks/base/core/java/android/app/StatusBarManager.java)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
deleted file mode 100644
index 54be9a6..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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;
-
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * NotificationEntryManager is responsible for the adding, removing, and updating of
- * {@link NotificationEntry}s. It also handles tasks such as their inflation and their interaction
- * with other Notification.*Manager objects.
- *
- * We track notification entries through this lifecycle:
- * 1. Pending
- * 2. Active
- * 3. Sorted / filtered (visible)
- *
- * Every entry spends some amount of time in the pending state, while it is being inflated. Once
- * inflated, an entry moves into the active state, where it _could_ potentially be shown to the
- * user. After an entry makes its way into the active state, we sort and filter the entire set to
- * repopulate the visible set.
- */
-public class NotificationEntryManager {
-
- private final NotificationEntryManagerLogger mLogger;
-
- /** Pending notifications are ones awaiting inflation */
- @VisibleForTesting
- protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
- /**
- * Active notifications have been inflated / prepared and could become visible, but may get
- * filtered out if for instance they are not for the current user
- */
- private final ArrayMap<String, NotificationEntry> mActiveNotifications = new ArrayMap<>();
- /** This is the list of "active notifications for this user in this context" */
- @VisibleForTesting
- protected final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
- private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
- private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
-
- /**
- * Injected constructor. See {@link NotificationsModule}.
- */
- public NotificationEntryManager(NotificationEntryManagerLogger logger) {
- mLogger = logger;
- }
-
- /** Adds a {@link NotificationEntryListener}. */
- public void addNotificationEntryListener(NotificationEntryListener listener) {
- mNotificationEntryListeners.add(listener);
- }
-
- /**
- * Removes a {@link NotificationEntryListener} previously registered via
- * {@link #addNotificationEntryListener(NotificationEntryListener)}.
- */
- public void removeNotificationEntryListener(NotificationEntryListener listener) {
- mNotificationEntryListeners.remove(listener);
- }
-
- /**
- * Update the notifications
- * @param reason why the notifications are updating
- */
- public void updateNotifications(String reason) {
- mLogger.logUseWhileNewPipelineActive("updateNotifications", reason);
- }
-
- /*
- * -----
- * Annexed from NotificationData below:
- * Some of these methods may be redundant but require some reworking to remove. For now
- * we'll try to keep the behavior the same and can simplify these interfaces in another pass
- */
-
- /** Resorts / filters the current notification set with the current RankingMap */
- public void reapplyFilterAndSort(String reason) {
- mLogger.logUseWhileNewPipelineActive("reapplyFilterAndSort", reason);
- }
-
- /** dump the current active notification list. Called from CentralSurfaces */
- public void dump(PrintWriter pw, String indent) {
- pw.println("NotificationEntryManager (Legacy)");
- int filteredLen = mSortedAndFiltered.size();
- pw.print(indent);
- pw.println("active notifications: " + filteredLen);
- int active;
- for (active = 0; active < filteredLen; active++) {
- NotificationEntry e = mSortedAndFiltered.get(active);
- dumpEntry(pw, indent, active, e);
- }
- synchronized (mActiveNotifications) {
- int totalLen = mActiveNotifications.size();
- pw.print(indent);
- pw.println("inactive notifications: " + (totalLen - active));
- int inactiveCount = 0;
- for (int i = 0; i < totalLen; i++) {
- NotificationEntry entry = mActiveNotifications.valueAt(i);
- if (!mSortedAndFiltered.contains(entry)) {
- dumpEntry(pw, indent, inactiveCount, entry);
- inactiveCount++;
- }
- }
- }
- }
-
- private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
- pw.print(indent);
- pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.getIcons().getStatusBarIcon());
- StatusBarNotification n = e.getSbn();
- pw.print(indent);
- pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
- + e.getRanking().getImportance());
- pw.print(indent);
- pw.println(" notification=" + n.getNotification());
- }
-
- public void addCollectionListener(@NonNull NotifCollectionListener listener) {
- mNotifCollectionListeners.add(listener);
- }
-
- public void removeCollectionListener(@NonNull NotifCollectionListener listener) {
- mNotifCollectionListeners.remove(listener);
- }
-
- /*
- * End annexation
- * -----
- */
-
-
- /**
- * Provides access to keyguard state and user settings dependent data.
- */
- public interface KeyguardEnvironment {
- /** true if the device is provisioned (should always be true in practice) */
- boolean isDeviceProvisioned();
- /** true if the notification is for the current profiles */
- boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
- }
-
- /**
- * Used when a notification is removed and it doesn't have a reason that maps to one of the
- * reasons defined in NotificationListenerService
- * (e.g. {@link NotificationListenerService#REASON_CANCEL})
- */
- public static final int UNDEFINED_DISMISS_REASON = 0;
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
deleted file mode 100644
index 52dcf02..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification
-
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
-import com.android.systemui.log.LogMessage
-import com.android.systemui.log.dagger.NotificationLog
-import com.android.systemui.util.Compile
-import javax.inject.Inject
-
-/** Logger for [NotificationEntryManager]. */
-class NotificationEntryManagerLogger @Inject constructor(
- notifPipelineFlags: NotifPipelineFlags,
- @NotificationLog private val buffer: LogBuffer
-) {
- private val devLoggingEnabled by lazy { notifPipelineFlags.isDevLoggingEnabled() }
-
- private inline fun devLog(
- level: LogLevel,
- initializer: LogMessage.() -> Unit,
- noinline printer: LogMessage.() -> String
- ) {
- if (Compile.IS_DEBUG && devLoggingEnabled) buffer.log(TAG, level, initializer, printer)
- }
-
- fun logNotifAdded(key: String) {
- devLog(INFO, {
- str1 = key
- }, {
- "NOTIF ADDED $str1"
- })
- }
-
- fun logNotifUpdated(key: String) {
- devLog(INFO, {
- str1 = key
- }, {
- "NOTIF UPDATED $str1"
- })
- }
-
- fun logInflationAborted(key: String, status: String, reason: String) {
- devLog(DEBUG, {
- str1 = key
- str2 = status
- str3 = reason
- }, {
- "NOTIF INFLATION ABORTED $str1 notifStatus=$str2 reason=$str3"
- })
- }
-
- fun logNotifInflated(key: String, isNew: Boolean) {
- devLog(DEBUG, {
- str1 = key
- bool1 = isNew
- }, {
- "NOTIF INFLATED $str1 isNew=$bool1}"
- })
- }
-
- fun logRemovalIntercepted(key: String) {
- devLog(INFO, {
- str1 = key
- }, {
- "NOTIF REMOVE INTERCEPTED for $str1"
- })
- }
-
- fun logLifetimeExtended(key: String, extenderName: String, status: String) {
- devLog(INFO, {
- str1 = key
- str2 = extenderName
- str3 = status
- }, {
- "NOTIF LIFETIME EXTENDED $str1 extender=$str2 status=$str3"
- })
- }
-
- fun logNotifRemoved(key: String, removedByUser: Boolean) {
- devLog(INFO, {
- str1 = key
- bool1 = removedByUser
- }, {
- "NOTIF REMOVED $str1 removedByUser=$bool1"
- })
- }
-
- fun logFilterAndSort(reason: String) {
- devLog(INFO, {
- str1 = reason
- }, {
- "FILTER AND SORT reason=$str1"
- })
- }
-
- fun logUseWhileNewPipelineActive(method: String, reason: String) {
- buffer.log(TAG, WARNING, {
- str1 = method
- str2 = reason
- }, {
- "While running New Pipeline: $str1(reason=$str2)"
- })
- }
-}
-
-private const val TAG = "NotificationEntryMgr"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
index 9d0f974..32150fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
@@ -20,11 +20,9 @@
import android.os.Parcelable
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
-import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -44,14 +42,10 @@
* In addition, notifications that have recently alerted aren't filtered. Tracking this in a way
* that involves the fewest pipeline invalidations requires some unfortunately complex logic.
*/
-// This class is a singleton so that the same instance can be accessed by both the old and new
-// pipelines
@CoordinatorScope
class SmartspaceDedupingCoordinator @Inject constructor(
private val statusBarStateController: SysuiStatusBarStateController,
private val smartspaceController: LockscreenSmartspaceController,
- private val notificationEntryManager: NotificationEntryManager,
- private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
private val notifPipeline: NotifPipeline,
@Main private val executor: DelayableExecutor,
private val clock: SystemClock
@@ -132,7 +126,6 @@
if (changed) {
filter.invalidateList("onNewSmartspaceTargets")
- notificationEntryManager.updateNotifications("Smartspace targets changed")
}
trackedSmartspaceTargets = newMap
@@ -169,7 +162,6 @@
target.cancelTimeoutRunnable = null
target.shouldFilter = true
filter.invalidateList("updateAlertException: ${entry.logKey}")
- notificationEntryManager.updateNotifications("deduping timeout expired")
}, alertExceptionExpires - now)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index d52f3c6..cdfd2f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -19,12 +19,11 @@
import android.annotation.NonNull;
import com.android.systemui.statusbar.notification.InflationException;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
/**
- * Used by the {@link NotificationEntryManager}. When notifications are added or updated, the binder
+ * Used by the {@link NotifInflater}. When notifications are added or updated, the binder
* is asked to (re)inflate and prepare their views. This inflation must occur off the main thread.
*/
public interface NotificationRowBinder {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
index 4ff6a64..fc7d682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
@@ -19,7 +19,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -29,9 +28,8 @@
* A notification collection that manages the list of {@link NotificationEntry}s that will be
* rendered.
*
- * TODO: (b/145659174) Once we fully switch off {@link NotificationEntryManager} to
- * {@link NotifPipeline}, we probably won't need this, but having it for now makes it easy to
- * switch between the two.
+ * TODO: (b/145659174) Once we fully migrate to {@link NotifPipeline}, we probably won't need this,
+ * but having it for now makes it easy to switch between the two.
*/
public interface CommonNotifCollection {
/**
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 9333c2a..da4cced 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
@@ -36,8 +36,6 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
@@ -106,14 +104,6 @@
@Binds
StackScrollAlgorithm.BypassController bindBypassController(KeyguardBypassController impl);
- /** Provides an instance of {@link NotificationEntryManager} */
- @SysUISingleton
- @Provides
- static NotificationEntryManager provideNotificationEntryManager(
- NotificationEntryManagerLogger logger) {
- return new NotificationEntryManager(logger);
- }
-
/** Provides an instance of {@link NotificationGutsManager} */
@SysUISingleton
@Provides
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 3b10b20..a5278c3d 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
@@ -24,7 +24,6 @@
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 java.io.PrintWriter
/**
* The master controller for all notifications-related work
@@ -42,9 +41,7 @@
bindRowCallback: NotificationRowBinderImpl.BindRowCallback
)
- fun requestNotificationUpdate(reason: String)
fun resetUserExpandedStates()
fun setNotificationSnoozed(sbn: StatusBarNotification, snoozeOption: SnoozeOption)
fun getActiveNotificationsCount(): Int
- fun dump(pw: PrintWriter, args: Array<String>, dumpTruck: Boolean)
}
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 be9f133..801e544 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
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.init
import android.service.notification.StatusBarNotification
+import com.android.systemui.ForegroundServiceNotificationListener
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
@@ -26,7 +27,6 @@
import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.NotificationClicker
-import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -43,7 +43,6 @@
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
-import java.io.PrintWriter
import java.util.Optional
import javax.inject.Inject
@@ -57,7 +56,6 @@
@SysUISingleton
class NotificationsControllerImpl @Inject constructor(
private val notificationListener: NotificationListener,
- private val entryManager: NotificationEntryManager,
private val commonNotifCollection: Lazy<CommonNotifCollection>,
private val notifPipeline: Lazy<NotifPipeline>,
private val notifLiveDataStore: NotifLiveDataStore,
@@ -72,6 +70,7 @@
private val animatedImageNotificationManager: AnimatedImageNotificationManager,
private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager,
private val bubblesOptional: Optional<Bubbles>,
+ private val fgsNotifListener: ForegroundServiceNotificationListener,
) : NotificationsController {
override fun initialize(
@@ -112,24 +111,11 @@
notificationsMediaManager.setUpWithPresenter(presenter)
notificationLogger.setUpWithContainer(listContainer)
peopleSpaceWidgetManager.attach(notificationListener)
- }
-
- override fun dump(
- pw: PrintWriter,
- args: Array<String>,
- dumpTruck: Boolean
- ) {
- if (dumpTruck) {
- entryManager.dump(pw, " ")
- }
+ fgsNotifListener.init()
}
// TODO: Convert all functions below this line into listeners instead of public methods
- override fun requestNotificationUpdate(reason: String) {
- entryManager.updateNotifications(reason)
- }
-
override fun resetUserExpandedStates() {
// TODO: this is a view thing that should be done through the views, but that means doing it
// both when this event is fired and any time a row is attached.
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 06a9eac..14856da 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
@@ -25,7 +25,6 @@
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 java.io.PrintWriter
import javax.inject.Inject
/**
@@ -48,9 +47,6 @@
notificationListener.registerAsSystemService()
}
- override fun requestNotificationUpdate(reason: String) {
- }
-
override fun resetUserExpandedStates() {
}
@@ -60,14 +56,4 @@
override fun getActiveNotificationsCount(): Int {
return 0
}
-
- override fun dump(
- pw: PrintWriter,
- args: Array<String>,
- dumpTruck: Boolean
- ) {
- pw.println()
- pw.println("Notification handling disabled")
- pw.println()
- }
}
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 8574f87..6138265 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
@@ -3455,6 +3455,8 @@
pw.print("visibility: " + getVisibility());
pw.print(", alpha: " + getAlpha());
pw.print(", translation: " + getTranslation());
+ pw.print(", Entry isDismissable: " + mEntry.isDismissable());
+ pw.print(", mOnUserInteractionCallback null: " + (mOnUserInteractionCallback == null));
pw.print(", removed: " + isRemoved());
pw.print(", expandAnimationRunning: " + mExpandAnimationRunning);
NotificationContentView showingLayout = getShowingLayout();
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 ec1e620..843a9ff 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
@@ -81,7 +81,6 @@
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -155,7 +154,6 @@
private final ScrimController mScrimController;
private final NotifPipeline mNotifPipeline;
private final NotifCollection mNotifCollection;
- private final NotificationEntryManager mNotificationEntryManager;
private final UiEventLogger mUiEventLogger;
private final NotificationRemoteInputManager mRemoteInputManager;
private final ShadeController mShadeController;
@@ -307,7 +305,6 @@
mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
mLockscreenUserManager.isAnyProfilePublicMode());
mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
- mNotificationEntryManager.updateNotifications("CentralSurfaces state changed");
}
};
@@ -634,7 +631,6 @@
@SilentHeader SectionHeaderController silentHeaderController,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
- NotificationEntryManager notificationEntryManager,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
ShadeTransitionController shadeTransitionController,
UiEventLogger uiEventLogger,
@@ -676,7 +672,6 @@
mSilentHeaderController = silentHeaderController;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
- mNotificationEntryManager = notificationEntryManager;
mUiEventLogger = uiEventLogger;
mRemoteInputManager = remoteInputManager;
mShadeController = shadeController;
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 353355b..eeed070 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
@@ -163,7 +163,7 @@
// which is more expensive.
if (shelfState.hidden) {
// When the shelf is hidden, it won't clip views, so we don't hide rows
- return;
+ continue;
}
final float shelfTop = shelfState.yTranslation;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 7bef844..87d1d66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -28,11 +28,8 @@
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -88,12 +85,6 @@
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
private static final int UDFPS_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
- private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- private static final VibrationEffect ERROR_VIBRATION_EFFECT =
- VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
- private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
@@ -169,7 +160,6 @@
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final SessionTracker mSessionTracker;
private final int mConsecutiveFpFailureThreshold;
- private final boolean mShouldVibrate;
private int mMode;
private BiometricSourceType mBiometricType;
private KeyguardViewController mKeyguardViewController;
@@ -311,8 +301,6 @@
mHandler = handler;
mConsecutiveFpFailureThreshold = resources.getInteger(
R.integer.fp_consecutive_failure_time_ms);
- mShouldVibrate = !(resources.getBoolean(
- com.android.internal.R.bool.system_server_plays_face_haptics));
mKeyguardBypassController = keyguardBypassController;
mKeyguardBypassController.setUnlockController(this);
mMetricsLogger = metricsLogger;
@@ -427,10 +415,10 @@
public void startWakeAndUnlock(BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
int mode = calculateMode(biometricSourceType, isStrongBiometric);
- if (BiometricSourceType.FACE == biometricSourceType && (mode == MODE_WAKE_AND_UNLOCK
+ if (mode == MODE_WAKE_AND_UNLOCK
|| mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING
- || mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER)) {
- vibrateSuccess();
+ || mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER) {
+ vibrateSuccess(biometricSourceType);
}
startWakeAndUnlock(mode);
}
@@ -672,10 +660,11 @@
}
// Suppress all face auth errors if fingerprint can be used to authenticate
- if (biometricSourceType == BiometricSourceType.FACE
+ if ((biometricSourceType == BiometricSourceType.FACE
&& !mUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())) {
- vibrateError();
+ KeyguardUpdateMonitor.getCurrentUser()))
+ || (biometricSourceType == BiometricSourceType.FINGERPRINT)) {
+ vibrateError(biometricSourceType);
}
cleanup();
@@ -704,24 +693,15 @@
cleanup();
}
- private void vibrateSuccess() {
- if (mShouldVibrate) {
- mVibratorHelper.vibrate(Process.myUid(),
- "com.android.systemui",
- SUCCESS_VIBRATION_EFFECT,
- getClass().getSimpleName() + "::success",
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
- }
+ //these haptics are for device-entry only
+ private void vibrateSuccess(BiometricSourceType type) {
+ mVibratorHelper.vibrateAuthSuccess(
+ getClass().getSimpleName() + ", type =" + type + "device-entry::success");
}
- private void vibrateError() {
- if (mShouldVibrate) {
- mVibratorHelper.vibrate(Process.myUid(),
- "com.android.systemui",
- ERROR_VIBRATION_EFFECT,
- getClass().getSimpleName() + "::error",
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
- }
+ private void vibrateError(BiometricSourceType type) {
+ mVibratorHelper.vibrateAuthError(
+ getClass().getSimpleName() + ", type =" + type + "device-entry::error");
}
private void cleanup() {
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 602971a..3259ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -40,6 +40,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.RemoteTransitionAdapter;
@@ -225,9 +226,12 @@
boolean isShadeDisabled();
- void requestNotificationUpdate(String reason);
-
- void requestFaceAuth(boolean userInitiatedRequest);
+ /**
+ * Request face auth to initiated
+ * @param userInitiatedRequest Whether this was a user initiated request
+ * @param reason Reason why face auth was triggered.
+ */
+ void requestFaceAuth(boolean userInitiatedRequest, @FaceAuthApiRequestReason String reason);
@Override
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
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 52a45d6..e30bc68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -58,8 +58,8 @@
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
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 a190485..7f2b6c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -119,6 +119,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
@@ -201,7 +202,6 @@
import com.android.systemui.statusbar.core.StatusBarInitializer;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.init.NotificationsController;
@@ -491,7 +491,6 @@
private boolean mExpandedVisible;
- protected final NotificationEntryManager mEntryManager;
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
private final PanelExpansionStateManager mPanelExpansionStateManager;
@@ -664,7 +663,6 @@
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
- NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationInterruptStateProvider notificationInterruptStateProvider,
@@ -751,7 +749,6 @@
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
- mEntryManager = notificationEntryManager;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
mNotificationInterruptStateProvider = notificationInterruptStateProvider;
@@ -822,11 +819,8 @@
mPanelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
- mBubbleExpandListener =
- (isExpanding, key) -> mContext.getMainExecutor().execute(() -> {
- mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
- updateScrimController();
- });
+ mBubbleExpandListener = (isExpanding, key) ->
+ mContext.getMainExecutor().execute(this::updateScrimController);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mActivityLaunchAnimator = activityLaunchAnimator;
@@ -1598,21 +1592,14 @@
}
/**
- * Request a notification update
- * @param reason why we're requesting a notification update
- */
- @Override
- public void requestNotificationUpdate(String reason) {
- mNotificationsController.requestNotificationUpdate(reason);
- }
-
- /**
* Asks {@link KeyguardUpdateMonitor} to run face auth.
*/
@Override
- public void requestFaceAuth(boolean userInitiatedRequest) {
+ public void requestFaceAuth(boolean userInitiatedRequest,
+ @FaceAuthApiRequestReason String reason) {
if (!mKeyguardStateController.canDismissLockScreen()) {
- mKeyguardUpdateMonitor.requestFaceAuth(userInitiatedRequest);
+ mKeyguardUpdateMonitor.requestFaceAuth(
+ userInitiatedRequest, reason);
}
}
@@ -2308,8 +2295,6 @@
mStatusBarKeyguardViewManager.dump(pw);
}
- mNotificationsController.dump(pw, args, DUMPTRUCK);
-
if (DEBUG_GESTURES) {
pw.print(" status bar gestures: ");
mGestureRec.dump(pw, args);
@@ -4213,14 +4198,6 @@
maybeEscalateHeadsUp();
}
}
-
- // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
- // KeyguardCoordinator
- @Override
- public void onStrongAuthStateChanged(int userId) {
- super.onStrongAuthStateChanged(userId);
- mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
- }
};
@@ -4409,7 +4386,6 @@
updateQsExpansionEnabled();
mKeyguardViewMediator.setDozing(mDozing);
- mNotificationsController.requestNotificationUpdate("onDozingChanged");
updateDozingState();
mDozeServiceHost.updateDozing();
updateScrimController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index deb6150..1169d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -29,13 +31,13 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.R;
import com.android.systemui.demomode.DemoMode;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
@@ -49,7 +51,6 @@
private final LinearLayout mStatusIcons;
private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>();
private final int mIconSize;
- private final FeatureFlags mFeatureFlags;
private StatusBarWifiView mWifiView;
private boolean mDemoMode;
@@ -57,14 +58,12 @@
public DemoStatusIcons(
LinearLayout statusIcons,
- int iconSize,
- FeatureFlags featureFlags
+ int iconSize
) {
super(statusIcons.getContext());
mStatusIcons = statusIcons;
mIconSize = iconSize;
mColor = DarkIconDispatcher.DEFAULT_ICON_TINT;
- mFeatureFlags = featureFlags;
if (statusIcons instanceof StatusIconContainer) {
setShouldRestrictIcons(((StatusIconContainer) statusIcons).isRestrictingIcons());
@@ -253,9 +252,13 @@
}
}
- public void addMobileView(MobileIconState state) {
+ /**
+ * Add a new mobile icon view
+ */
+ public void addMobileView(MobileIconState state, Context mobileContext) {
Log.d(TAG, "addMobileView: ");
- StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, state.slot);
+ StatusBarMobileView view = StatusBarMobileView
+ .fromContext(mobileContext, state.slot);
view.applyMobileState(state);
view.setStaticDrawableColor(mColor);
@@ -265,19 +268,24 @@
addView(view, getChildCount(), createLayoutParams());
}
- public void updateMobileState(MobileIconState state) {
- Log.d(TAG, "updateMobileState: ");
- // If the view for this subId exists already, use it
+ /**
+ * Apply an update to a mobile icon view for the given {@link MobileIconState}. For
+ * compatibility with {@link MobileContextProvider}, we have to recreate the view every time we
+ * update it, since the context (and thus the {@link Configuration}) may have changed
+ */
+ public void updateMobileState(MobileIconState state, Context mobileContext) {
+ Log.d(TAG, "updateMobileState: " + state);
+
+ // The mobile config provided by MobileContextProvider could have changed; always recreate
for (int i = 0; i < mMobileViews.size(); i++) {
StatusBarMobileView view = mMobileViews.get(i);
if (view.getState().subId == state.subId) {
- view.applyMobileState(state);
- return;
+ removeView(view);
}
}
- // Else we have to add it
- addMobileView(state);
+ // Add the replacement or new icon
+ addMobileView(state, mobileContext);
}
public void onRemoveIcon(StatusIconDisplayable view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 80c3e6c..ddff7d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -226,7 +226,6 @@
}
mStatusBarStateController.setIsDozing(dozing);
- mNotificationShadeWindowViewController.setDozing(dozing);
if (mFoldAodAnimationController != null) {
mFoldAodAnimationController.setIsDozing(dozing);
}
@@ -310,7 +309,6 @@
public void dozeTimeTick() {
mNotificationPanel.dozeTimeTick();
mAuthController.dozeTimeTick();
- mNotificationShadeWindowViewController.dozeTimeTick();
if (mAmbientIndicationContainer instanceof DozeReceiver) {
((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
deleted file mode 100644
index 2c4fc6c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ /dev/null
@@ -1,60 +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.statusbar.phone;
-
-import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.MULTIUSER_DEBUG;
-
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-
-import javax.inject.Inject;
-
-@SysUISingleton
-public class KeyguardEnvironmentImpl implements KeyguardEnvironment {
-
- private static final String TAG = "KeyguardEnvironmentImpl";
-
- private final NotificationLockscreenUserManager mLockscreenUserManager;
- private final DeviceProvisionedController mDeviceProvisionedController;
-
- @Inject
- public KeyguardEnvironmentImpl(
- NotificationLockscreenUserManager notificationLockscreenUserManager,
- DeviceProvisionedController deviceProvisionedController) {
- mLockscreenUserManager = notificationLockscreenUserManager;
- mDeviceProvisionedController = deviceProvisionedController;
- }
-
- @Override // NotificationEntryManager.KeyguardEnvironment
- public boolean isDeviceProvisioned() {
- return mDeviceProvisionedController.isDeviceProvisioned();
- }
-
- @Override // NotificationEntryManager.KeyguardEnvironment
- public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
- final int notificationUserId = n.getUserId();
- if (DEBUG && MULTIUSER_DEBUG) {
- Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n,
- mLockscreenUserManager.getCurrentUserId(), notificationUserId));
- }
- return mLockscreenUserManager.isCurrentProfile(notificationUserId);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 3f8e97f1..b58dbe2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -22,6 +22,7 @@
import android.hardware.TriggerEvent
import android.hardware.TriggerEventListener
import com.android.keyguard.ActiveUnlockConfig
+import com.android.keyguard.FaceAuthApiRequestReason
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.CoreStartable
@@ -71,7 +72,10 @@
// Not listening anymore since trigger events unregister themselves
isListening = false
updateListeningState()
- keyguardUpdateMonitor.requestFaceAuth(true)
+ keyguardUpdateMonitor.requestFaceAuth(
+ true,
+ FaceAuthApiRequestReason.PICK_UP_GESTURE_TRIGGERED
+ )
keyguardUpdateMonitor.requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE,
"KeyguardLiftController")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 290df3e..48e58fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -33,6 +33,7 @@
import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -127,7 +128,7 @@
private final DateFormatUtil mDateFormatUtil;
private final TelecomManager mTelecomManager;
- private final Handler mHandler = new Handler();
+ private final Handler mHandler;
private final CastController mCast;
private final HotspotController mHotspot;
private final NextAlarmController mNextAlarmController;
@@ -166,7 +167,7 @@
@Inject
public PhoneStatusBarPolicy(StatusBarIconController iconController,
CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher,
- @UiBackground Executor uiBgExecutor, @Main Resources resources,
+ @UiBackground Executor uiBgExecutor, @Main Looper looper, @Main Resources resources,
CastController castController, HotspotController hotspotController,
BluetoothController bluetoothController, NextAlarmController nextAlarmController,
UserInfoController userInfoController, RotationLockController rotationLockController,
@@ -185,6 +186,7 @@
mIconController = iconController;
mCommandQueue = commandQueue;
mBroadcastDispatcher = broadcastDispatcher;
+ mHandler = new Handler(looper);
mResources = resources;
mCast = castController;
mHotspot = hotspotController;
@@ -332,6 +334,7 @@
mRotationLockController.addCallback(this);
mBluetooth.addCallback(this);
mProvisionedController.addCallback(this);
+ mCurrentUserSetup = mProvisionedController.isCurrentUserSetup();
mZenController.addCallback(this);
mCast.addCallback(mCastCallback);
mHotspot.addCallback(mHotspotCallback);
@@ -562,6 +565,7 @@
mHandler.post(() -> {
updateAlarm();
updateManagedProfile();
+ onUserSetupChanged();
});
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index ebfbf54..ae201e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -120,7 +120,6 @@
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- mNotificationsController.requestNotificationUpdate("onHeadsUpStateChanged");
if (mStatusBarStateController.isDozing() && isHeadsUp) {
entry.setPulseSuppressed(false);
mDozeServiceHost.fireNotificationPulse(entry);
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 8273d57..bd99713 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -36,7 +36,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoModeCommandReceiver;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.BaseStatusBarWifiView;
@@ -44,6 +43,7 @@
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
@@ -70,6 +70,7 @@
void addIconGroup(IconManager iconManager);
/** */
void removeIconGroup(IconManager iconManager);
+
/** Refresh the state of an IconManager by recreating the views */
void refreshIconGroup(IconManager iconManager);
/** */
@@ -82,21 +83,25 @@
void setSignalIcon(String slot, WifiIconState state);
/** */
void setMobileIcons(String slot, List<MobileIconState> states);
+
/**
* Display the no calling & SMS icons.
*/
void setCallStrengthIcons(String slot, List<CallIndicatorIconState> states);
+
/**
* Display the no calling & SMS icons.
*/
void setNoCallingIcons(String slot, List<CallIndicatorIconState> states);
+
public void setIconVisibility(String slot, boolean b);
/**
* Sets the live region mode for the icon
- * @see android.view.View#setAccessibilityLiveRegion(int)
- * @param slot Icon slot to set region for
+ *
+ * @param slot Icon slot to set region for
* @param accessibilityLiveRegion live region mode for the icon
+ * @see android.view.View#setAccessibilityLiveRegion(int)
*/
void setIconAccessibilityLiveRegion(String slot, int accessibilityLiveRegion);
@@ -115,8 +120,8 @@
static ArraySet<String> getIconHideList(Context context, String hideListStr) {
ArraySet<String> ret = new ArraySet<>();
String[] hideList = hideListStr == null
- ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude)
- : hideListStr.split(",");
+ ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude)
+ : hideListStr.split(",");
for (String slot : hideList) {
if (!TextUtils.isEmpty(slot)) {
ret.add(slot);
@@ -134,11 +139,14 @@
public DarkIconManager(
LinearLayout linearLayout,
- FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
Provider<WifiViewModel> wifiViewModelProvider,
+ MobileContextProvider mobileContextProvider,
DarkIconDispatcher darkIconDispatcher) {
- super(linearLayout, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
+ super(linearLayout,
+ statusBarPipelineFlags,
+ wifiViewModelProvider,
+ mobileContextProvider);
mIconHPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_padding);
mDarkIconDispatcher = darkIconDispatcher;
@@ -195,41 +203,49 @@
@SysUISingleton
public static class Factory {
- private final FeatureFlags mFeatureFlags;
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private final Provider<WifiViewModel> mWifiViewModelProvider;
+ private final MobileContextProvider mMobileContextProvider;
private final DarkIconDispatcher mDarkIconDispatcher;
@Inject
public Factory(
- FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
Provider<WifiViewModel> wifiViewModelProvider,
+ MobileContextProvider mobileContextProvider,
DarkIconDispatcher darkIconDispatcher) {
- mFeatureFlags = featureFlags;
mStatusBarPipelineFlags = statusBarPipelineFlags;
mWifiViewModelProvider = wifiViewModelProvider;
+ mMobileContextProvider = mobileContextProvider;
mDarkIconDispatcher = darkIconDispatcher;
}
public DarkIconManager create(LinearLayout group) {
return new DarkIconManager(
- group, mFeatureFlags, mStatusBarPipelineFlags, mWifiViewModelProvider,
+ group,
+ mStatusBarPipelineFlags,
+ mWifiViewModelProvider,
+ mMobileContextProvider,
mDarkIconDispatcher);
}
}
}
- /** */
+ /**
+ *
+ */
class TintedIconManager extends IconManager {
private int mColor;
public TintedIconManager(
ViewGroup group,
- FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
- Provider<WifiViewModel> wifiViewModelProvider) {
- super(group, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
+ Provider<WifiViewModel> wifiViewModelProvider,
+ MobileContextProvider mobileContextProvider) {
+ super(group,
+ statusBarPipelineFlags,
+ wifiViewModelProvider,
+ mobileContextProvider);
}
@Override
@@ -261,23 +277,26 @@
@SysUISingleton
public static class Factory {
- private final FeatureFlags mFeatureFlags;
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private final Provider<WifiViewModel> mWifiViewModelProvider;
+ private final MobileContextProvider mMobileContextProvider;
@Inject
public Factory(
- FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
- Provider<WifiViewModel> wifiViewModelProvider) {
- mFeatureFlags = featureFlags;
+ Provider<WifiViewModel> wifiViewModelProvider,
+ MobileContextProvider mobileContextProvider) {
mStatusBarPipelineFlags = statusBarPipelineFlags;
mWifiViewModelProvider = wifiViewModelProvider;
+ mMobileContextProvider = mobileContextProvider;
}
public TintedIconManager create(ViewGroup group) {
return new TintedIconManager(
- group, mFeatureFlags, mStatusBarPipelineFlags, mWifiViewModelProvider);
+ group,
+ mStatusBarPipelineFlags,
+ mWifiViewModelProvider,
+ mMobileContextProvider);
}
}
}
@@ -287,9 +306,9 @@
*/
class IconManager implements DemoModeCommandReceiver {
protected final ViewGroup mGroup;
- private final FeatureFlags mFeatureFlags;
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private final Provider<WifiViewModel> mWifiViewModelProvider;
+ private final MobileContextProvider mMobileContextProvider;
protected final Context mContext;
protected final int mIconSize;
// Whether or not these icons show up in dumpsys
@@ -305,13 +324,13 @@
public IconManager(
ViewGroup group,
- FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
- Provider<WifiViewModel> wifiViewModelProvider) {
+ Provider<WifiViewModel> wifiViewModelProvider,
+ MobileContextProvider mobileContextProvider) {
mGroup = group;
- mFeatureFlags = featureFlags;
mStatusBarPipelineFlags = statusBarPipelineFlags;
mWifiViewModelProvider = wifiViewModelProvider;
+ mMobileContextProvider = mobileContextProvider;
mContext = group.getContext();
mIconSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
@@ -403,12 +422,15 @@
@VisibleForTesting
protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
- StatusBarMobileView view = onCreateStatusBarMobileView(slot);
+ // Use the `subId` field as a key to query for the correct context
+ StatusBarMobileView view = onCreateStatusBarMobileView(state.subId, slot);
view.applyMobileState(state);
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
- mDemoStatusIcons.addMobileView(state);
+ Context mobileContext = mMobileContextProvider
+ .getMobileContextForSub(state.subId, mContext);
+ mDemoStatusIcons.addMobileView(state, mobileContext);
}
return view;
}
@@ -427,8 +449,10 @@
mContext, slot, mWifiViewModelProvider.get());
}
- private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
- StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
+ private StatusBarMobileView onCreateStatusBarMobileView(int subId, String slot) {
+ Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
+ StatusBarMobileView view = StatusBarMobileView
+ .fromContext(mobileContext, slot);
return view;
}
@@ -516,7 +540,9 @@
}
if (mIsInDemoMode) {
- mDemoStatusIcons.updateMobileState(state);
+ Context mobileContext = mMobileContextProvider
+ .getMobileContextForSub(state.subId, mContext);
+ mDemoStatusIcons.updateMobileState(state, mobileContext);
}
}
@@ -553,7 +579,7 @@
}
protected DemoStatusIcons createDemoStatusIcons() {
- return new DemoStatusIcons((LinearLayout) mGroup, mIconSize, mFeatureFlags);
+ return new DemoStatusIcons((LinearLayout) mGroup, mIconSize);
}
}
}
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 e9771d2..0dd1f44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -589,7 +589,7 @@
public void reset(boolean hideBouncerWhenShowing) {
if (mShowing) {
// Hide quick settings.
- mNotificationPanelViewController.resetViews(/* animate= */ true);
+ mNotificationPanelViewController.resetViews(/* animate= */ !mOccluded);
// Hide bouncer and quick-quick settings.
if (mOccluded && !mDozing) {
mCentralSurfaces.hideKeyguard();
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 b1104ae..a1e0c50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -33,8 +33,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
@@ -183,10 +181,6 @@
mLockscreenUserManager.setUpWithPresenter(this);
mGutsManager.setUpWithPresenter(
this, mNotifListContainer, mOnSettingsClickListener);
- // ForegroundServiceNotificationListener adds its listener in its constructor
- // but we need to request it here in order for it to be instantiated.
- // TODO: figure out how to do this correctly once Dependency.get() is gone.
- Dependency.get(ForegroundServiceNotificationListener.class);
onUserSwitched(mLockscreenUserManager.getCurrentUserId());
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 5fd72b7..891f657 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -53,10 +53,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.DisableFlagsLogger.DisableState;
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
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 9ae378f..28ed080 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
@@ -19,7 +19,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.CollapsedSbFragmentLog
-import com.android.systemui.statusbar.DisableFlagsLogger
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
/** Used by [CollapsedStatusBarFragment] to log messages to a [LogBuffer]. */
@@ -60,4 +60,4 @@
}
}
-private const val TAG = "CollapsedSbFragment"
\ No newline at end of file
+private const val TAG = "CollapsedSbFragment"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 88eccb5..681dc6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -18,6 +18,8 @@
import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
import dagger.Binds
@@ -34,5 +36,8 @@
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
@Binds
+ abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
+
+ @Binds
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
index 2a89309..88d8a86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
@@ -34,7 +34,7 @@
/**
* Logs a change in one of the **raw inputs** to the connectivity pipeline.
*/
- fun logInputChange(callbackName: String, changeInfo: String) {
+ fun logInputChange(callbackName: String, changeInfo: String?) {
buffer.log(
SB_LOGGING_TAG,
LogLevel.INFO,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt
new file mode 100644
index 0000000..d52d0fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.pipeline.shared.data.model
+
+import android.content.Context
+import com.android.internal.R
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A container for all the different types of connectivity slots: wifi, mobile, etc.
+ */
+@SysUISingleton
+class ConnectivitySlots @Inject constructor(context: Context) {
+ private val airplaneSlot: String = context.getString(R.string.status_bar_airplane)
+ private val mobileSlot: String = context.getString(R.string.status_bar_mobile)
+ private val wifiSlot: String = context.getString(R.string.status_bar_wifi)
+ private val ethernetSlot: String = context.getString(R.string.status_bar_ethernet)
+
+ private val slotByName: Map<String, ConnectivitySlot> = mapOf(
+ airplaneSlot to ConnectivitySlot.AIRPLANE,
+ mobileSlot to ConnectivitySlot.MOBILE,
+ wifiSlot to ConnectivitySlot.WIFI,
+ ethernetSlot to ConnectivitySlot.ETHERNET
+ )
+
+ /**
+ * Given a string name of a slot, returns the instance of [ConnectivitySlot] that it corresponds
+ * to, or null if we couldn't find that slot name.
+ */
+ fun getSlotFromName(slotName: String): ConnectivitySlot? {
+ return slotByName[slotName]
+ }
+}
+
+/** The different types of connectivity slots. */
+enum class ConnectivitySlot {
+ AIRPLANE,
+ ETHERNET,
+ MOBILE,
+ WIFI,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
new file mode 100644
index 0000000..6b1750d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.pipeline.shared.data.repository
+
+import android.content.Context
+import androidx.annotation.ArrayRes
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+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.dump.DumpManager
+import com.android.systemui.statusbar.phone.StatusBarIconController
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
+import com.android.systemui.tuner.TunerService
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Provides data related to the connectivity state that needs to be shared across multiple different
+ * types of connectivity (wifi, mobile, ethernet, etc.)
+ */
+interface ConnectivityRepository {
+ /**
+ * Observable for the current set of connectivity icons that should be force-hidden.
+ */
+ val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>>
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class ConnectivityRepositoryImpl @Inject constructor(
+ private val connectivitySlots: ConnectivitySlots,
+ context: Context,
+ dumpManager: DumpManager,
+ logger: ConnectivityPipelineLogger,
+ @Application scope: CoroutineScope,
+ tunerService: TunerService,
+) : ConnectivityRepository, Dumpable {
+ init {
+ dumpManager.registerDumpable("$SB_LOGGING_TAG:ConnectivityRepository", this)
+ }
+
+ // The default set of hidden icons to use if we don't get any from [TunerService].
+ private val defaultHiddenIcons: Set<ConnectivitySlot> =
+ context.resources.getStringArray(DEFAULT_HIDDEN_ICONS_RESOURCE)
+ .asList()
+ .toSlotSet(connectivitySlots)
+
+ override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = conflatedCallbackFlow {
+ val callback = object : TunerService.Tunable {
+ override fun onTuningChanged(key: String, newHideList: String?) {
+ if (key != HIDDEN_ICONS_TUNABLE_KEY) {
+ return
+ }
+ logger.logInputChange("onTuningChanged", newHideList)
+
+ val outputList = newHideList?.split(",")?.toSlotSet(connectivitySlots)
+ ?: defaultHiddenIcons
+ trySend(outputList)
+ }
+ }
+ tunerService.addTunable(callback, HIDDEN_ICONS_TUNABLE_KEY)
+
+ awaitClose { tunerService.removeTunable(callback) }
+ }
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = defaultHiddenIcons
+ )
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.apply {
+ println("defaultHiddenIcons=$defaultHiddenIcons")
+ }
+ }
+
+ companion object {
+ @VisibleForTesting
+ internal const val HIDDEN_ICONS_TUNABLE_KEY = StatusBarIconController.ICON_HIDE_LIST
+ @VisibleForTesting
+ @ArrayRes
+ internal val DEFAULT_HIDDEN_ICONS_RESOURCE = R.array.config_statusBarIconsToExclude
+
+ /** Converts a list of string slot names to a set of [ConnectivitySlot] instances. */
+ private fun List<String>.toSlotSet(
+ connectivitySlots: ConnectivitySlots
+ ): Set<ConnectivitySlot> {
+ return this
+ .filter { it.isNotBlank() }
+ .mapNotNull { connectivitySlots.getSlotFromName(it) }
+ .toSet()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index afe19af..952525d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -18,6 +18,8 @@
import android.net.wifi.WifiManager
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import javax.inject.Inject
@@ -33,9 +35,10 @@
*/
@SysUISingleton
class WifiInteractor @Inject constructor(
- repository: WifiRepository,
+ connectivityRepository: ConnectivityRepository,
+ wifiRepository: WifiRepository,
) {
- private val ssid: Flow<String?> = repository.wifiNetwork.map { info ->
+ private val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
when (info) {
is WifiNetworkModel.Inactive -> null
is WifiNetworkModel.CarrierMerged -> null
@@ -49,10 +52,16 @@
}
/** Our current wifi network. See [WifiNetworkModel]. */
- val wifiNetwork: Flow<WifiNetworkModel> = repository.wifiNetwork
+ val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
+
+ /** True if we're configured to force-hide the wifi icon and false otherwise. */
+ val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
+ it.contains(ConnectivitySlot.WIFI)
+ }
/** True if our wifi network has activity in (download), and false otherwise. */
- val hasActivityIn: Flow<Boolean> = combine(repository.wifiActivity, ssid) { activity, ssid ->
+ val hasActivityIn: Flow<Boolean> =
+ combine(wifiRepository.wifiActivity, ssid) { activity, ssid ->
activity.hasActivityIn && ssid != null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 7607ddf..4fad327 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -24,6 +24,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
+import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
import kotlinx.coroutines.InternalCoroutinesApi
@@ -54,14 +55,15 @@
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- viewModel.wifiIconResId.distinctUntilChanged().collect { iconResId ->
- iconView.setImageDrawable(
- if (iconResId != null && iconResId > 0) {
- iconView.context.getDrawable(iconResId)
- } else {
- null
- }
- )
+ viewModel.wifiIcon.distinctUntilChanged().collect { wifiIcon ->
+ // TODO(b/238425913): Right now, if !isVisible, there's just an empty space
+ // where the wifi icon would be. We need to pipe isVisible through to
+ // [ModernStatusBarWifiView.isIconVisible], which is what actually makes
+ // the view GONE.
+ view.isVisible = wifiIcon != null
+ wifiIcon?.let {
+ IconViewBinder.bind(wifiIcon, iconView)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 4fdcc44..3eff0bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -18,6 +18,7 @@
import android.graphics.Color
import androidx.annotation.DrawableRes
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
@@ -29,6 +30,7 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -46,7 +48,7 @@
* The drawable resource ID to use for the wifi icon. Null if we shouldn't display any icon.
*/
@DrawableRes
- val wifiIconResId: Flow<Int?> = interactor.wifiNetwork.map {
+ private val iconResId: Flow<Int?> = interactor.wifiNetwork.map {
when (it) {
is WifiNetworkModel.CarrierMerged -> null
is WifiNetworkModel.Inactive -> WIFI_NO_NETWORK
@@ -59,6 +61,25 @@
}
}
+ /**
+ * The wifi icon that should be displayed. Null if we shouldn't display any icon.
+ */
+ val wifiIcon: Flow<Icon?> = combine(
+ interactor.isForceHidden,
+ iconResId
+ ) { isForceHidden, iconResId ->
+ when {
+ isForceHidden ||
+ iconResId == null ||
+ iconResId <= 0 -> null
+ // TODO(b/238425913): Implement the content description.
+ else -> Icon.Resource(iconResId, /* contentDescription= */ null)
+ }
+ }
+
+ /**
+ * True if the activity in icon should be displayed and false otherwise.
+ */
val isActivityInVisible: Flow<Boolean>
get() =
if (!constants.shouldShowActivityConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index e2d1601..3b8ed33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -86,6 +86,7 @@
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
import com.android.systemui.user.data.source.UserRecord;
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -100,14 +101,20 @@
import javax.inject.Inject;
+import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+
/**
* Keeps a list of all users on the device for user switching.
*/
@SysUISingleton
public class UserSwitcherController implements Dumpable {
- public static final float USER_SWITCH_ENABLED_ALPHA = 1.0f;
- public static final float USER_SWITCH_DISABLED_ALPHA = 0.38f;
+ public static final float USER_SWITCH_ENABLED_ALPHA =
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA;
+ public static final float USER_SWITCH_DISABLED_ALPHA =
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA;
private static final String TAG = "UserSwitcherController";
private static final boolean DEBUG = false;
@@ -155,7 +162,8 @@
private boolean mSimpleUserSwitcher;
// When false, there won't be any visual affordance to add a new user from the keyguard even if
// the user is unlocked
- private boolean mAddUsersFromLockScreen;
+ private final MutableStateFlow<Boolean> mAddUsersFromLockScreen =
+ StateFlowKt.MutableStateFlow(false);
private boolean mUserSwitcherEnabled;
@VisibleForTesting
boolean mPauseRefreshUsers;
@@ -258,8 +266,11 @@
@Override
public void onChange(boolean selfChange) {
mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
- mAddUsersFromLockScreen = mGlobalSettings.getIntForUser(
- Settings.Global.ADD_USERS_WHEN_LOCKED, 0, UserHandle.USER_SYSTEM) != 0;
+ mAddUsersFromLockScreen.setValue(
+ mGlobalSettings.getIntForUser(
+ Settings.Global.ADD_USERS_WHEN_LOCKED,
+ 0,
+ UserHandle.USER_SYSTEM) != 0);
mUserSwitcherEnabled = mGlobalSettings.getIntForUser(
Settings.Global.USER_SWITCHER_ENABLED, 0, UserHandle.USER_SYSTEM) != 0;
refreshUsers(UserHandle.USER_NULL);
@@ -323,7 +334,6 @@
}
mForcePictureLoadForUserId.clear();
- final boolean addUsersWhenLocked = mAddUsersFromLockScreen;
mBgExecutor.execute(() -> {
List<UserInfo> infos = mUserManager.getAliveUsers();
if (infos == null) {
@@ -434,7 +444,7 @@
}
boolean anyoneCanCreateUsers() {
- return systemCanCreateUsers() && mAddUsersFromLockScreen;
+ return systemCanCreateUsers() && mAddUsersFromLockScreen.getValue();
}
boolean canCreateGuest(boolean hasExistingGuest) {
@@ -450,7 +460,7 @@
}
boolean createIsRestricted() {
- return !mAddUsersFromLockScreen;
+ return !mAddUsersFromLockScreen.getValue();
}
boolean canCreateSupervisedUser() {
@@ -516,17 +526,48 @@
return null;
}
+ /**
+ * Notifies that a user has been selected.
+ *
+ * <p>This will trigger the right user journeys to create a guest user, switch users, and/or
+ * navigate to the correct destination.
+ *
+ * <p>If a user with the given ID is not found, this method is a no-op.
+ *
+ * @param userId The ID of the user to switch to.
+ * @param dialogShower An optional {@link DialogShower} in case we need to show dialogs.
+ */
+ public void onUserSelected(int userId, @Nullable DialogShower dialogShower) {
+ UserRecord userRecord = mUsers.stream()
+ .filter(x -> x.resolveId() == userId)
+ .findFirst()
+ .orElse(null);
+ if (userRecord == null) {
+ return;
+ }
+
+ onUserListItemClicked(userRecord, dialogShower);
+ }
+
+ /** Whether it is allowed to add users while the device is locked. */
+ public Flow<Boolean> getAddUsersFromLockScreen() {
+ return mAddUsersFromLockScreen;
+ }
+
+ /** Returns {@code true} if the guest user is configured to always be present on the device. */
+ public boolean isGuestUserAutoCreated() {
+ return mGuestUserAutoCreated;
+ }
+
+ /** Returns {@code true} if the guest user is currently being reset. */
+ public boolean isGuestUserResetting() {
+ return mGuestIsResetting.get();
+ }
+
@VisibleForTesting
void onUserListItemClicked(UserRecord record, DialogShower dialogShower) {
if (record.isGuest && record.info == null) {
- // No guest user. Create one.
- createGuestAsync(guestId -> {
- // guestId may be USER_NULL if we haven't reloaded the user list yet.
- if (guestId != UserHandle.USER_NULL) {
- mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
- onUserListItemClicked(guestId, record, dialogShower);
- }
- });
+ createAndSwitchToGuestUser(dialogShower);
} else if (record.isAddUser) {
showAddUserDialog(dialogShower);
} else if (record.isAddSupervisedUser) {
@@ -604,7 +645,23 @@
}
}
- private void showAddUserDialog(DialogShower dialogShower) {
+ /**
+ * Creates and switches to the guest user.
+ */
+ public void createAndSwitchToGuestUser(@Nullable DialogShower dialogShower) {
+ createGuestAsync(guestId -> {
+ // guestId may be USER_NULL if we haven't reloaded the user list yet.
+ if (guestId != UserHandle.USER_NULL) {
+ mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
+ onUserListItemClicked(guestId, UserRecord.createForGuest(), dialogShower);
+ }
+ });
+ }
+
+ /**
+ * Shows the add user dialog.
+ */
+ public void showAddUserDialog(@Nullable DialogShower dialogShower) {
if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
mAddUserDialog.cancel();
}
@@ -620,7 +677,10 @@
}
}
- private void startSupervisedUserActivity() {
+ /**
+ * Starts an activity to add a supervised user to the device.
+ */
+ public void startSupervisedUserActivity() {
final Intent intent = new Intent()
.setAction(UserManager.ACTION_CREATE_SUPERVISED_USER)
.setPackage(mCreateSupervisedUserPackage)
@@ -772,7 +832,7 @@
* Removes guest user and switches to target user. The guest must be the current user and its id
* must be {@code guestUserId}.
*
- * <p>If {@code targetUserId} is {@link UserHandle.USER_NULL}, then create a new guest user in
+ * <p>If {@code targetUserId} is {@link UserHandle#USER_NULL}, then create a new guest user in
* the foreground, and immediately switch to it. This is used for wiping the current guest and
* replacing it with a new one.
*
@@ -782,11 +842,11 @@
* <p>If device is configured with {@link
* com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a
* new one is created in the background. This has no effect if {@code targetUserId} is {@link
- * UserHandle.USER_NULL}.
+ * UserHandle#USER_NULL}.
*
* @param guestUserId id of the guest user to remove
* @param targetUserId id of the user to switch to after guest is removed. If {@link
- * UserHandle.USER_NULL}, then switch immediately to the newly created guest user.
+ * UserHandle#USER_NULL}, then switch immediately to the newly created guest user.
*/
public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) {
UserInfo currentUser = mUserTracker.getUserInfo();
@@ -839,7 +899,7 @@
* user.
*
* @param guestUserId user id of the guest user to exit
- * @param targetUserId user id of the guest user to exit, set to UserHandle.USER_NULL when
+ * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when
* target user id is not known
* @param forceRemoveGuestOnExit true: remove guest before switching user,
* false: remove guest only if its ephemeral, else keep guest
@@ -952,7 +1012,7 @@
* {@link UserManager} to create a new one.
*
* @return The multi-user user ID of the newly created guest user, or
- * {@link UserHandle.USER_NULL} if the guest couldn't be created.
+ * {@link UserHandle#USER_NULL} if the guest couldn't be created.
*/
public @UserIdInt int createGuest() {
UserInfo guest;
@@ -1062,38 +1122,11 @@
}
public String getName(Context context, UserRecord item) {
- if (item.isGuest) {
- if (item.isCurrent) {
- return context.getString(
- com.android.settingslib.R.string.guest_exit_quick_settings_button);
- } else {
- if (item.info != null) {
- return context.getString(com.android.internal.R.string.guest_name);
- } else {
- if (mController.mGuestUserAutoCreated) {
- // If mGuestIsResetting=true, we expect the guest user to be created
- // shortly, so display a "Resetting guest..." as an indicator that we
- // are busy. Otherwise, if mGuestIsResetting=false, we probably failed
- // to create a guest at some point. In this case, always show guest
- // nickname instead of "Add guest" to make it seem as though the device
- // always has a guest ready for use.
- return context.getString(
- mController.mGuestIsResetting.get()
- ? com.android.settingslib.R.string.guest_resetting
- : com.android.internal.R.string.guest_name);
- } else {
- // we always show "guest" as string, instead of "add guest"
- return context.getString(com.android.internal.R.string.guest_name);
- }
- }
- }
- } else if (item.isAddUser) {
- return context.getString(com.android.settingslib.R.string.user_add_user);
- } else if (item.isAddSupervisedUser) {
- return context.getString(R.string.add_user_supervised);
- } else {
- return item.info.name;
- }
+ return LegacyUserUiHelper.getUserRecordName(
+ context,
+ item,
+ mController.isGuestUserAutoCreated(),
+ mController.isGuestUserResetting());
}
protected static ColorFilter getDisabledUserAvatarColorFilter() {
@@ -1103,17 +1136,8 @@
}
protected static Drawable getIconDrawable(Context context, UserRecord item) {
- int iconRes;
- if (item.isAddUser) {
- iconRes = R.drawable.ic_add;
- } else if (item.isGuest) {
- iconRes = R.drawable.ic_account_circle;
- } else if (item.isAddSupervisedUser) {
- iconRes = R.drawable.ic_add_supervised_user;
- } else {
- iconRes = R.drawable.ic_avatar_user;
- }
-
+ int iconRes = LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
+ item.isAddUser, item.isGuest, item.isAddSupervisedUser);
return context.getDrawable(iconRes);
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index abffe555..27746c0 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -56,13 +56,11 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -152,10 +150,6 @@
abstract DockManager bindDockManager(DockManagerImpl dockManager);
@Binds
- abstract NotificationEntryManager.KeyguardEnvironment bindKeyguardEnvironment(
- KeyguardEnvironmentImpl keyguardEnvironment);
-
- @Binds
abstract ShadeController provideShadeController(ShadeControllerImpl shadeController);
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
index 469d54f..5b522dc 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import com.android.settingslib.users.EditUserInfoController;
+import com.android.systemui.user.data.repository.UserRepositoryModule;
import dagger.Binds;
import dagger.Module;
@@ -29,7 +30,11 @@
/**
* Dagger module for User related classes.
*/
-@Module
+@Module(
+ includes = {
+ UserRepositoryModule.class,
+ }
+)
public abstract class UserModule {
private static final String FILE_PROVIDER_AUTHORITY = "com.android.systemui.fileprovider";
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index ff0f0d4..8a51cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -27,6 +27,7 @@
import android.os.Bundle
import android.os.UserManager
import android.provider.Settings
+import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
@@ -37,6 +38,7 @@
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.constraintlayout.helper.widget.Flow
+import androidx.lifecycle.ViewModelProvider
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.util.UserIcons
import com.android.settingslib.Utils
@@ -44,6 +46,8 @@
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.settings.UserTracker
@@ -52,6 +56,9 @@
import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA
import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA
import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.ui.binder.UserSwitcherViewBinder
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import dagger.Lazy
import javax.inject.Inject
import kotlin.math.ceil
@@ -63,11 +70,12 @@
class UserSwitcherActivity @Inject constructor(
private val userSwitcherController: UserSwitcherController,
private val broadcastDispatcher: BroadcastDispatcher,
- private val layoutInflater: LayoutInflater,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val userManager: UserManager,
- private val userTracker: UserTracker
+ private val userTracker: UserTracker,
+ private val flags: FeatureFlags,
+ private val viewModelFactory: Lazy<UserSwitcherViewModel.Factory>,
) : ComponentActivity() {
private lateinit var parent: UserSwitcherRootView
@@ -93,119 +101,31 @@
false /* isAddSupervisedUser */
)
- private val adapter = object : BaseUserAdapter(userSwitcherController) {
- override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
- val item = getItem(position)
- var view = convertView as ViewGroup?
- if (view == null) {
- view = layoutInflater.inflate(
- R.layout.user_switcher_fullscreen_item,
- parent,
- false
- ) as ViewGroup
- }
- (view.getChildAt(0) as ImageView).apply {
- setImageDrawable(getDrawable(item))
- }
- (view.getChildAt(1) as TextView).apply {
- setText(getName(getContext(), item))
- }
-
- view.setEnabled(item.isSwitchToEnabled)
- view.setAlpha(
- if (view.isEnabled()) {
- USER_SWITCH_ENABLED_ALPHA
- } else {
- USER_SWITCH_DISABLED_ALPHA
- }
- )
- view.setTag(USER_VIEW)
- return view
- }
-
- override fun getName(context: Context, item: UserRecord): String {
- return if (item == manageUserRecord) {
- getString(R.string.manage_users)
- } else {
- super.getName(context, item)
- }
- }
-
- fun findUserIcon(item: UserRecord): Drawable {
- if (item == manageUserRecord) {
- return getDrawable(R.drawable.ic_manage_users)
- }
- if (item.info == null) {
- return getIconDrawable(this@UserSwitcherActivity, item)
- }
- val userIcon = userManager.getUserIcon(item.info.id)
- if (userIcon != null) {
- return BitmapDrawable(userIcon)
- }
- return UserIcons.getDefaultUserIcon(resources, item.info.id, false)
- }
-
- fun getTotalUserViews(): Int {
- return users.count { item ->
- !doNotRenderUserView(item)
- }
- }
-
- fun doNotRenderUserView(item: UserRecord): Boolean {
- return item.isAddUser ||
- item.isAddSupervisedUser ||
- item.isGuest && item.info == null
- }
-
- private fun getDrawable(item: UserRecord): Drawable {
- var drawable = if (item.isGuest) {
- getDrawable(R.drawable.ic_account_circle)
- } else {
- findUserIcon(item)
- }
- drawable.mutate()
-
- if (!item.isCurrent && !item.isSwitchToEnabled) {
- drawable.setTint(
- resources.getColor(
- R.color.kg_user_switcher_restricted_avatar_icon_color,
- getTheme()
- )
- )
- }
-
- val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate()
- as LayerDrawable
- if (item == userSwitcherController.getCurrentUserRecord()) {
- (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
- val stroke = resources
- .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
- val color = Utils.getColorAttrDefaultColor(
- this@UserSwitcherActivity,
- com.android.internal.R.attr.colorAccentPrimary
- )
-
- setStroke(stroke, color)
- }
- }
-
- ld.setDrawableByLayerId(R.id.user_avatar, drawable)
- return ld
- }
-
- override fun notifyDataSetChanged() {
- super.notifyDataSetChanged()
- buildUserViews()
- }
- }
+ private val adapter: UserAdapter by lazy { UserAdapter() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.user_switcher_fullscreen)
- window.decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+ if (isUsingModernArchitecture()) {
+ Log.d(TAG, "Using modern architecture.")
+ val viewModel = ViewModelProvider(
+ this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
+ UserSwitcherViewBinder.bind(
+ view = requireViewById(R.id.user_switcher_root),
+ viewModel = viewModel,
+ lifecycleOwner = this,
+ layoutInflater = layoutInflater,
+ falsingCollector = falsingCollector,
+ onFinish = this::finish,
+ )
+ return
+ } else {
+ Log.d(TAG, "Not using modern architecture.")
+ }
parent = requireViewById<UserSwitcherRootView>(R.id.user_switcher_root)
@@ -346,11 +266,18 @@
}
override fun onBackPressed() {
+ if (isUsingModernArchitecture()) {
+ return super.onBackPressed()
+ }
+
finish()
}
override fun onDestroy() {
super.onDestroy()
+ if (isUsingModernArchitecture()) {
+ return
+ }
broadcastDispatcher.unregisterReceiver(broadcastReceiver)
userTracker.removeCallback(userSwitchedCallback)
@@ -376,6 +303,10 @@
return if (userCount < 5) 4 else ceil(userCount / 2.0).toInt()
}
+ private fun isUsingModernArchitecture(): Boolean {
+ return flags.isEnabled(Flags.MODERN_USER_SWITCHER_ACTIVITY)
+ }
+
private class ItemAdapter(
val parentContext: Context,
val resource: Int,
@@ -398,4 +329,114 @@
return view
}
}
+
+ private inner class UserAdapter : BaseUserAdapter(userSwitcherController) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val item = getItem(position)
+ var view = convertView as ViewGroup?
+ if (view == null) {
+ view = layoutInflater.inflate(
+ R.layout.user_switcher_fullscreen_item,
+ parent,
+ false
+ ) as ViewGroup
+ }
+ (view.getChildAt(0) as ImageView).apply {
+ setImageDrawable(getDrawable(item))
+ }
+ (view.getChildAt(1) as TextView).apply {
+ setText(getName(getContext(), item))
+ }
+
+ view.setEnabled(item.isSwitchToEnabled)
+ view.setAlpha(
+ if (view.isEnabled()) {
+ USER_SWITCH_ENABLED_ALPHA
+ } else {
+ USER_SWITCH_DISABLED_ALPHA
+ }
+ )
+ view.setTag(USER_VIEW)
+ return view
+ }
+
+ override fun getName(context: Context, item: UserRecord): String {
+ return if (item == manageUserRecord) {
+ getString(R.string.manage_users)
+ } else {
+ super.getName(context, item)
+ }
+ }
+
+ fun findUserIcon(item: UserRecord): Drawable {
+ if (item == manageUserRecord) {
+ return getDrawable(R.drawable.ic_manage_users)
+ }
+ if (item.info == null) {
+ return getIconDrawable(this@UserSwitcherActivity, item)
+ }
+ val userIcon = userManager.getUserIcon(item.info.id)
+ if (userIcon != null) {
+ return BitmapDrawable(userIcon)
+ }
+ return UserIcons.getDefaultUserIcon(resources, item.info.id, false)
+ }
+
+ fun getTotalUserViews(): Int {
+ return users.count { item ->
+ !doNotRenderUserView(item)
+ }
+ }
+
+ fun doNotRenderUserView(item: UserRecord): Boolean {
+ return item.isAddUser ||
+ item.isAddSupervisedUser ||
+ item.isGuest && item.info == null
+ }
+
+ private fun getDrawable(item: UserRecord): Drawable {
+ var drawable = if (item.isGuest) {
+ getDrawable(R.drawable.ic_account_circle)
+ } else {
+ findUserIcon(item)
+ }
+ drawable.mutate()
+
+ if (!item.isCurrent && !item.isSwitchToEnabled) {
+ drawable.setTint(
+ resources.getColor(
+ R.color.kg_user_switcher_restricted_avatar_icon_color,
+ getTheme()
+ )
+ )
+ }
+
+ val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate()
+ as LayerDrawable
+ if (item == userSwitcherController.getCurrentUserRecord()) {
+ (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
+ val stroke = resources
+ .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
+ val color = Utils.getColorAttrDefaultColor(
+ this@UserSwitcherActivity,
+ com.android.internal.R.attr.colorAccentPrimary
+ )
+
+ setStroke(stroke, color)
+ }
+ }
+
+ ld.setDrawableByLayerId(R.id.user_avatar, drawable)
+ return ld
+ }
+
+ override fun notifyDataSetChanged() {
+ super.notifyDataSetChanged()
+ buildUserViews()
+ }
+ }
+
+ companion object {
+ private const val TAG = "UserSwitcherActivity"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
new file mode 100644
index 0000000..305b5ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.user.data.repository
+
+import android.content.Context
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.os.UserManager
+import androidx.appcompat.content.res.AppCompatResources
+import com.android.internal.util.UserIcons
+import com.android.systemui.R
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Acts as source of truth for user related data.
+ *
+ * Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about
+ * upstream changes.
+ */
+interface UserRepository {
+ /** List of all users on the device. */
+ val users: Flow<List<UserModel>>
+
+ /** The currently-selected user. */
+ val selectedUser: Flow<UserModel>
+
+ /** List of available user-related actions. */
+ val actions: Flow<List<UserActionModel>>
+
+ /** Whether actions are available even when locked. */
+ val isActionableWhenLocked: Flow<Boolean>
+
+ /** Whether the device is configured to always have a guest user available. */
+ val isGuestUserAutoCreated: Boolean
+
+ /** Whether the guest user is currently being reset. */
+ val isGuestUserResetting: Boolean
+}
+
+@SysUISingleton
+class UserRepositoryImpl
+@Inject
+constructor(
+ @Application private val appContext: Context,
+ private val manager: UserManager,
+ controller: UserSwitcherController,
+) : UserRepository {
+
+ private val userRecords: Flow<List<UserRecord>> = conflatedCallbackFlow {
+ fun send() {
+ trySendWithFailureLogging(
+ controller.users,
+ TAG,
+ )
+ }
+
+ val callback = UserSwitcherController.UserSwitchCallback { send() }
+
+ controller.addUserSwitchCallback(callback)
+ send()
+
+ awaitClose { controller.removeUserSwitchCallback(callback) }
+ }
+
+ override val users: Flow<List<UserModel>> =
+ userRecords.map { records -> records.filter { it.isUser() }.map { it.toUserModel() } }
+
+ override val selectedUser: Flow<UserModel> =
+ users.map { users -> users.first { user -> user.isSelected } }
+
+ override val actions: Flow<List<UserActionModel>> =
+ userRecords.map { records -> records.filter { it.isNotUser() }.map { it.toActionModel() } }
+
+ override val isActionableWhenLocked: Flow<Boolean> = controller.addUsersFromLockScreen
+
+ override val isGuestUserAutoCreated: Boolean = controller.isGuestUserAutoCreated
+
+ override val isGuestUserResetting: Boolean = controller.isGuestUserResetting
+
+ private fun UserRecord.isUser(): Boolean {
+ return when {
+ isAddUser -> false
+ isAddSupervisedUser -> false
+ isGuest -> info != null
+ else -> true
+ }
+ }
+
+ private fun UserRecord.isNotUser(): Boolean {
+ return !isUser()
+ }
+
+ private fun UserRecord.toUserModel(): UserModel {
+ return UserModel(
+ id = resolveId(),
+ name = getUserName(this),
+ image = getUserImage(this),
+ isSelected = isCurrent,
+ isSelectable = isSwitchToEnabled || isGuest,
+ )
+ }
+
+ private fun UserRecord.toActionModel(): UserActionModel {
+ return when {
+ isAddUser -> UserActionModel.ADD_USER
+ isAddSupervisedUser -> UserActionModel.ADD_SUPERVISED_USER
+ isGuest -> UserActionModel.ENTER_GUEST_MODE
+ else -> error("Don't know how to convert to UserActionModel: $this")
+ }
+ }
+
+ private fun getUserName(record: UserRecord): Text {
+ val resourceId: Int? = LegacyUserUiHelper.getGuestUserRecordNameResourceId(record)
+ return if (resourceId != null) {
+ Text.Resource(resourceId)
+ } else {
+ Text.Loaded(checkNotNull(record.info).name)
+ }
+ }
+
+ private fun getUserImage(record: UserRecord): Drawable {
+ if (record.isGuest) {
+ return checkNotNull(
+ AppCompatResources.getDrawable(appContext, R.drawable.ic_account_circle)
+ )
+ }
+
+ val userId = checkNotNull(record.info?.id)
+ return manager.getUserIcon(userId)?.let { userSelectedIcon ->
+ BitmapDrawable(userSelectedIcon)
+ }
+ ?: UserIcons.getDefaultUserIcon(appContext.resources, userId, /* light= */ false)
+ }
+
+ companion object {
+ private const val TAG = "UserRepository"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt
similarity index 61%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
copy to packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt
index e62a63a..18ae107 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt
@@ -12,20 +12,15 @@
* WITHOUT WARRANTIES OR CONDITIONS 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;
+package com.android.systemui.user.data.repository
-import android.os.SystemProperties;
+import dagger.Binds
+import dagger.Module
-/**
- * Constants for desktop mode feature
- */
-public class DesktopModeConstants {
-
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+@Module
+interface UserRepositoryModule {
+ @Binds fun bindRepository(impl: UserRepositoryImpl): UserRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
index 6ab6d7d..cf6da9a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
@@ -20,38 +20,29 @@
import android.graphics.Bitmap
import android.os.UserHandle
-/**
- * Encapsulates raw data for a user or an option item related to managing users on the device.
- */
+/** Encapsulates raw data for a user or an option item related to managing users on the device. */
data class UserRecord(
/** Relevant user information. If `null`, this record is not a user but an option item. */
- @JvmField
- val info: UserInfo?,
+ @JvmField val info: UserInfo? = null,
/** An image representing the user. */
- @JvmField
- val picture: Bitmap?,
+ @JvmField val picture: Bitmap? = null,
/** Whether this record represents an option to switch to a guest user. */
- @JvmField
- val isGuest: Boolean,
+ @JvmField val isGuest: Boolean = false,
/** Whether this record represents the currently-selected user. */
- @JvmField
- val isCurrent: Boolean,
+ @JvmField val isCurrent: Boolean = false,
/** Whether this record represents an option to add another user to the device. */
- @JvmField
- val isAddUser: Boolean,
- /** If true, the record is only visible to the owner and only when unlocked. */
- @JvmField
- val isRestricted: Boolean,
- /** Whether it is possible to switch to this user. */
- @JvmField
- val isSwitchToEnabled: Boolean,
- /** Whether this record represents an option to add another supervised user to the device. */
- @JvmField
- val isAddSupervisedUser: Boolean,
-) {
+ @JvmField val isAddUser: Boolean = false,
/**
- * Returns a new instance of [UserRecord] with its [isCurrent] set to the given value.
+ * If true, the record is only available if unlocked or if the user has granted permission to
+ * access this user action whilst on the device is locked.
*/
+ @JvmField val isRestricted: Boolean = false,
+ /** Whether it is possible to switch to this user. */
+ @JvmField val isSwitchToEnabled: Boolean = false,
+ /** Whether this record represents an option to add another supervised user to the device. */
+ @JvmField val isAddSupervisedUser: Boolean = false,
+) {
+ /** Returns a new instance of [UserRecord] with its [isCurrent] set to the given value. */
fun copyWithIsCurrent(isCurrent: Boolean): UserRecord {
return copy(isCurrent = isCurrent)
}
@@ -67,4 +58,11 @@
info.id
}
}
+
+ companion object {
+ @JvmStatic
+ fun createForGuest(): UserRecord {
+ return UserRecord(isGuest = true)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
new file mode 100644
index 0000000..3c5b969
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.user.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic to interact with user data and systems. */
+@SysUISingleton
+class UserInteractor
+@Inject
+constructor(
+ repository: UserRepository,
+ private val controller: UserSwitcherController,
+ private val activityStarter: ActivityStarter,
+ keyguardInteractor: KeyguardInteractor,
+) {
+ /** List of current on-device users to select from. */
+ val users: Flow<List<UserModel>> = repository.users
+
+ /** The currently-selected user. */
+ val selectedUser: Flow<UserModel> = repository.selectedUser
+
+ /** List of user-switcher related actions that are available. */
+ val actions: Flow<List<UserActionModel>> =
+ combine(
+ repository.isActionableWhenLocked,
+ keyguardInteractor.isKeyguardShowing,
+ ) { isActionableWhenLocked, isLocked ->
+ isActionableWhenLocked || !isLocked
+ }
+ .flatMapLatest { isActionable ->
+ if (isActionable) {
+ repository.actions.map { actions ->
+ actions +
+ if (actions.isNotEmpty()) {
+ // If we have actions, we add NAVIGATE_TO_USER_MANAGEMENT because
+ // that's a user
+ // switcher specific action that is not known to the our data source
+ // or other
+ // features.
+ listOf(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
+ } else {
+ // If no actions, don't add the navigate action.
+ emptyList()
+ }
+ }
+ } else {
+ // If not actionable it means that we're not allowed to show actions when locked
+ // and we
+ // are locked. Therefore, we should show no actions.
+ flowOf(emptyList())
+ }
+ }
+
+ /** Whether the device is configured to always have a guest user available. */
+ val isGuestUserAutoCreated: Boolean = repository.isGuestUserAutoCreated
+
+ /** Whether the guest user is currently being reset. */
+ val isGuestUserResetting: Boolean = repository.isGuestUserResetting
+
+ /** Switches to the user with the given user ID. */
+ fun selectUser(
+ userId: Int,
+ ) {
+ controller.onUserSelected(userId, /* dialogShower= */ null)
+ }
+
+ /** Executes the given action. */
+ fun executeAction(action: UserActionModel) {
+ when (action) {
+ UserActionModel.ENTER_GUEST_MODE -> controller.createAndSwitchToGuestUser(null)
+ UserActionModel.ADD_USER -> controller.showAddUserDialog(null)
+ UserActionModel.ADD_SUPERVISED_USER -> controller.startSupervisedUserActivity()
+ UserActionModel.NAVIGATE_TO_USER_MANAGEMENT ->
+ activityStarter.startActivity(
+ Intent(Settings.ACTION_USER_SETTINGS),
+ /* dismissShade= */ false,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
new file mode 100644
index 0000000..18369d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.user.legacyhelper.ui
+
+import android.content.Context
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import com.android.systemui.R
+import com.android.systemui.user.data.source.UserRecord
+import kotlin.math.ceil
+
+/**
+ * Defines utility functions for helping with legacy UI code for users.
+ *
+ * We need these to avoid code duplication between logic inside the UserSwitcherController and in
+ * modern architecture classes such as repositories, interactors, and view-models. If we ever
+ * simplify UserSwitcherController (or delete it), the code here could be moved into its call-sites.
+ */
+object LegacyUserUiHelper {
+
+ /** Returns the maximum number of columns for user items in the user switcher. */
+ fun getMaxUserSwitcherItemColumns(userCount: Int): Int {
+ // TODO(b/243844097): remove this once we remove the old user switcher implementation.
+ return if (userCount < 5) {
+ 4
+ } else {
+ ceil(userCount / 2.0).toInt()
+ }
+ }
+
+ @JvmStatic
+ @DrawableRes
+ fun getUserSwitcherActionIconResourceId(
+ isAddUser: Boolean,
+ isGuest: Boolean,
+ isAddSupervisedUser: Boolean,
+ ): Int {
+ return if (isAddUser) {
+ R.drawable.ic_add
+ } else if (isGuest) {
+ R.drawable.ic_account_circle
+ } else if (isAddSupervisedUser) {
+ R.drawable.ic_add_supervised_user
+ } else {
+ R.drawable.ic_avatar_user
+ }
+ }
+
+ @JvmStatic
+ fun getUserRecordName(
+ context: Context,
+ record: UserRecord,
+ isGuestUserAutoCreated: Boolean,
+ isGuestUserResetting: Boolean,
+ ): String {
+ val resourceId: Int? = getGuestUserRecordNameResourceId(record)
+ return when {
+ resourceId != null -> context.getString(resourceId)
+ record.info != null -> record.info.name
+ else ->
+ context.getString(
+ getUserSwitcherActionTextResourceId(
+ isGuest = record.isGuest,
+ isGuestUserAutoCreated = isGuestUserAutoCreated,
+ isGuestUserResetting = isGuestUserResetting,
+ isAddUser = record.isAddUser,
+ isAddSupervisedUser = record.isAddSupervisedUser,
+ )
+ )
+ }
+ }
+
+ /**
+ * Returns the resource ID for a string for the name of the guest user.
+ *
+ * If the given record is not the guest user, returns `null`.
+ */
+ @StringRes
+ fun getGuestUserRecordNameResourceId(record: UserRecord): Int? {
+ return when {
+ record.isGuest && record.isCurrent ->
+ com.android.settingslib.R.string.guest_exit_quick_settings_button
+ record.isGuest && record.info != null -> com.android.internal.R.string.guest_name
+ else -> null
+ }
+ }
+
+ @JvmStatic
+ @StringRes
+ fun getUserSwitcherActionTextResourceId(
+ isGuest: Boolean,
+ isGuestUserAutoCreated: Boolean,
+ isGuestUserResetting: Boolean,
+ isAddUser: Boolean,
+ isAddSupervisedUser: Boolean,
+ ): Int {
+ check(isGuest || isAddUser || isAddSupervisedUser)
+
+ return when {
+ isGuest && isGuestUserAutoCreated && isGuestUserResetting ->
+ com.android.settingslib.R.string.guest_resetting
+ isGuest && isGuestUserAutoCreated -> com.android.internal.R.string.guest_name
+ isGuest -> com.android.internal.R.string.guest_name
+ isAddUser -> com.android.settingslib.R.string.user_add_user
+ isAddSupervisedUser -> R.string.add_user_supervised
+ else -> error("This should never happen!")
+ }
+ }
+
+ /** Alpha value to apply to a user view in the user switcher when it's selectable. */
+ const val USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA = 1.0f
+
+ /** Alpha value to apply to a user view in the user switcher when it's not selectable. */
+ const val USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA = 0.38f
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt
similarity index 61%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
copy to packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt
index e62a63a..823bf74 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt
@@ -12,20 +12,14 @@
* WITHOUT WARRANTIES OR CONDITIONS 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;
+package com.android.systemui.user.shared.model
-import android.os.SystemProperties;
-
-/**
- * Constants for desktop mode feature
- */
-public class DesktopModeConstants {
-
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+enum class UserActionModel {
+ ENTER_GUEST_MODE,
+ ADD_USER,
+ ADD_SUPERVISED_USER,
+ NAVIGATE_TO_USER_MANAGEMENT,
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/shared/model/UserModel.kt b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserModel.kt
new file mode 100644
index 0000000..bf7977a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserModel.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.user.shared.model
+
+import android.graphics.drawable.Drawable
+import com.android.systemui.common.shared.model.Text
+
+/** Represents a single user on the device. */
+data class UserModel(
+ /** ID of the user, unique across all users on this device. */
+ val id: Int,
+ /** Human-facing name for this user. */
+ val name: Text,
+ /** Human-facing image for this user. */
+ val image: Drawable,
+ /** Whether this user is the currently-selected user. */
+ val isSelected: Boolean,
+ /** Whether this use is selectable. A non-selectable user cannot be switched to. */
+ val isSelectable: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
new file mode 100644
index 0000000..83a3d0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
@@ -0,0 +1,220 @@
+/*
+ * 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.user.ui.binder
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.constraintlayout.helper.widget.Flow as FlowWidget
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Gefingerpoken
+import com.android.systemui.R
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.user.UserSwitcherPopupMenu
+import com.android.systemui.user.UserSwitcherRootView
+import com.android.systemui.user.ui.viewmodel.UserActionViewModel
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import com.android.systemui.util.children
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
+
+/** Binds a user switcher to its view-model. */
+object UserSwitcherViewBinder {
+
+ private const val USER_VIEW_TAG = "user_view"
+
+ /** Binds the given view to the given view-model. */
+ fun bind(
+ view: ViewGroup,
+ viewModel: UserSwitcherViewModel,
+ lifecycleOwner: LifecycleOwner,
+ layoutInflater: LayoutInflater,
+ falsingCollector: FalsingCollector,
+ onFinish: () -> Unit,
+ ) {
+ val rootView: UserSwitcherRootView = view.requireViewById(R.id.user_switcher_root)
+ val flowWidget: FlowWidget = view.requireViewById(R.id.flow)
+ val addButton: View = view.requireViewById(R.id.add)
+ val cancelButton: View = view.requireViewById(R.id.cancel)
+ val popupMenuAdapter = MenuAdapter(layoutInflater)
+ var popupMenu: UserSwitcherPopupMenu? = null
+
+ rootView.touchHandler =
+ object : Gefingerpoken {
+ override fun onTouchEvent(ev: MotionEvent?): Boolean {
+ falsingCollector.onTouchEvent(ev)
+ return false
+ }
+ }
+ addButton.setOnClickListener { viewModel.onOpenMenuButtonClicked() }
+ cancelButton.setOnClickListener { viewModel.onCancelButtonClicked() }
+
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.isFinishRequested
+ .filter { it }
+ .collect {
+ onFinish()
+ viewModel.onFinished()
+ }
+ }
+ }
+ }
+
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch { viewModel.isOpenMenuButtonVisible.collect { addButton.isVisible = it } }
+
+ launch {
+ viewModel.isMenuVisible.collect { isVisible ->
+ if (isVisible && popupMenu?.isShowing != true) {
+ popupMenu?.dismiss()
+ // Use post to make sure we show the popup menu *after* the activity is
+ // ready to show one to avoid a WindowManager$BadTokenException.
+ view.post {
+ popupMenu =
+ createAndShowPopupMenu(
+ context = view.context,
+ anchorView = addButton,
+ adapter = popupMenuAdapter,
+ onDismissed = viewModel::onMenuClosed,
+ )
+ }
+ } else if (!isVisible && popupMenu?.isShowing == true) {
+ popupMenu?.dismiss()
+ popupMenu = null
+ }
+ }
+ }
+
+ launch {
+ viewModel.menu.collect { menuViewModels ->
+ popupMenuAdapter.setItems(menuViewModels)
+ }
+ }
+
+ launch {
+ viewModel.maximumUserColumns.collect { maximumColumns ->
+ flowWidget.setMaxElementsWrap(maximumColumns)
+ }
+ }
+
+ launch {
+ viewModel.users.collect { users ->
+ val viewPool =
+ view.children.filter { it.tag == USER_VIEW_TAG }.toMutableList()
+ viewPool.forEach { view.removeView(it) }
+ users.forEach { userViewModel ->
+ val userView =
+ if (viewPool.isNotEmpty()) {
+ viewPool.removeAt(0)
+ } else {
+ val inflatedView =
+ layoutInflater.inflate(
+ R.layout.user_switcher_fullscreen_item,
+ view,
+ false,
+ )
+ inflatedView.tag = USER_VIEW_TAG
+ inflatedView
+ }
+ userView.id = View.generateViewId()
+ view.addView(userView)
+ flowWidget.addView(userView)
+ UserViewBinder.bind(
+ view = userView,
+ viewModel = userViewModel,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun createAndShowPopupMenu(
+ context: Context,
+ anchorView: View,
+ adapter: MenuAdapter,
+ onDismissed: () -> Unit,
+ ): UserSwitcherPopupMenu {
+ return UserSwitcherPopupMenu(context).apply {
+ this.anchorView = anchorView
+ setAdapter(adapter)
+ setOnDismissListener { onDismissed() }
+ setOnItemClickListener { _, _, position, _ ->
+ val itemPositionExcludingHeader = position - 1
+ adapter.getItem(itemPositionExcludingHeader).onClicked()
+ }
+
+ show()
+ }
+ }
+
+ /** Adapter for the menu that can be opened. */
+ private class MenuAdapter(
+ private val layoutInflater: LayoutInflater,
+ ) : BaseAdapter() {
+
+ private val items = mutableListOf<UserActionViewModel>()
+
+ override fun getCount(): Int {
+ return items.size
+ }
+
+ override fun getItem(position: Int): UserActionViewModel {
+ return items[position]
+ }
+
+ override fun getItemId(position: Int): Long {
+ return getItem(position).viewKey
+ }
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view =
+ convertView
+ ?: layoutInflater.inflate(
+ R.layout.user_switcher_fullscreen_popup_item,
+ parent,
+ false
+ )
+ val viewModel = getItem(position)
+ view.requireViewById<ImageView>(R.id.icon).setImageResource(viewModel.iconResourceId)
+ view.requireViewById<TextView>(R.id.text).text =
+ view.resources.getString(viewModel.textResourceId)
+ return view
+ }
+
+ fun setItems(items: List<UserActionViewModel>) {
+ this.items.clear()
+ this.items.addAll(items)
+ notifyDataSetChanged()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt
new file mode 100644
index 0000000..e78807e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.user.ui.binder
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.view.View
+import android.widget.ImageView
+import androidx.core.content.res.ResourcesCompat
+import com.android.settingslib.Utils
+import com.android.systemui.R
+import com.android.systemui.common.ui.binder.TextViewBinder
+import com.android.systemui.user.ui.viewmodel.UserViewModel
+
+/** Binds a user view to its view-model. */
+object UserViewBinder {
+ /** Binds the given view to the given view-model. */
+ fun bind(view: View, viewModel: UserViewModel) {
+ TextViewBinder.bind(view.requireViewById(R.id.user_switcher_text), viewModel.name)
+ view
+ .requireViewById<ImageView>(R.id.user_switcher_icon)
+ .setImageDrawable(getSelectableDrawable(view.context, viewModel))
+ view.alpha = viewModel.alpha
+ if (viewModel.onClicked != null) {
+ view.setOnClickListener { viewModel.onClicked.invoke() }
+ } else {
+ view.setOnClickListener(null)
+ }
+ }
+
+ private fun getSelectableDrawable(context: Context, viewModel: UserViewModel): Drawable {
+ val layerDrawable =
+ checkNotNull(
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.user_switcher_icon_large,
+ context.theme,
+ )
+ )
+ .mutate() as LayerDrawable
+ if (viewModel.isSelectionMarkerVisible) {
+ (layerDrawable.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
+ val stroke =
+ context.resources.getDimensionPixelSize(
+ R.dimen.user_switcher_icon_selected_width
+ )
+ val color =
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.colorAccentPrimary
+ )
+
+ setStroke(stroke, color)
+ }
+ }
+
+ layerDrawable.setDrawableByLayerId(R.id.user_avatar, viewModel.image)
+ return layerDrawable
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserActionViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserActionViewModel.kt
new file mode 100644
index 0000000..149b1ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserActionViewModel.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.user.ui.viewmodel
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+
+/** Models UI state for an action that can be performed on a user. */
+data class UserActionViewModel(
+ /**
+ * Key to use with the view or compose system to keep track of the view/composable across
+ * changes to the collection of [UserActionViewModel] instances.
+ */
+ val viewKey: Long,
+ @DrawableRes val iconResourceId: Int,
+ @StringRes val textResourceId: Int,
+ val onClicked: () -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
new file mode 100644
index 0000000..66ce01b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.user.ui.viewmodel
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.R
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+/** Models UI state for the user switcher feature. */
+class UserSwitcherViewModel
+private constructor(
+ private val userInteractor: UserInteractor,
+ private val powerInteractor: PowerInteractor,
+) : ViewModel() {
+
+ /** On-device users. */
+ val users: Flow<List<UserViewModel>> =
+ userInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
+
+ /** The maximum number of columns that the user selection grid should use. */
+ val maximumUserColumns: Flow<Int> =
+ users.map { LegacyUserUiHelper.getMaxUserSwitcherItemColumns(it.size) }
+
+ /** Whether the button to open the user action menu is visible. */
+ val isOpenMenuButtonVisible: Flow<Boolean> = userInteractor.actions.map { it.isNotEmpty() }
+
+ private val _isMenuVisible = MutableStateFlow(false)
+ /**
+ * Whether the user action menu should be shown. Once the action menu is dismissed/closed, the
+ * consumer must invoke [onMenuClosed].
+ */
+ val isMenuVisible: Flow<Boolean> = _isMenuVisible
+ /** The user action menu. */
+ val menu: Flow<List<UserActionViewModel>> =
+ userInteractor.actions.map { actions -> actions.map { action -> toViewModel(action) } }
+
+ private val hasCancelButtonBeenClicked = MutableStateFlow(false)
+
+ /**
+ * Whether the observer should finish the experience. Once consumed, [onFinished] must be called
+ * by the consumer.
+ */
+ val isFinishRequested: Flow<Boolean> = createFinishRequestedFlow()
+
+ /** Notifies that the user has clicked the cancel button. */
+ fun onCancelButtonClicked() {
+ hasCancelButtonBeenClicked.value = true
+ }
+
+ /**
+ * Notifies that the user experience is finished.
+ *
+ * Call this after consuming [isFinishRequested] with a `true` value in order to mark it as
+ * consumed such that the next consumer doesn't immediately finish itself.
+ */
+ fun onFinished() {
+ hasCancelButtonBeenClicked.value = false
+ }
+
+ /** Notifies that the user has clicked the "open menu" button. */
+ fun onOpenMenuButtonClicked() {
+ _isMenuVisible.value = true
+ }
+
+ /**
+ * Notifies that the user has dismissed or closed the user action menu.
+ *
+ * Call this after consuming [isMenuVisible] with a `true` value in order to reset it to `false`
+ * such that the next consumer doesn't immediately show the menu again.
+ */
+ fun onMenuClosed() {
+ _isMenuVisible.value = false
+ }
+
+ private fun createFinishRequestedFlow(): Flow<Boolean> {
+ var mostRecentSelectedUserId: Int? = null
+ var mostRecentIsInteractive: Boolean? = null
+
+ return combine(
+ // When the user is switched, we should finish.
+ userInteractor.selectedUser
+ .map { it.id }
+ .map {
+ val selectedUserChanged =
+ mostRecentSelectedUserId != null && mostRecentSelectedUserId != it
+ mostRecentSelectedUserId = it
+ selectedUserChanged
+ },
+ // When the screen turns off, we should finish.
+ powerInteractor.isInteractive.map {
+ val screenTurnedOff = mostRecentIsInteractive == true && !it
+ mostRecentIsInteractive = it
+ screenTurnedOff
+ },
+ // When the cancel button is clicked, we should finish.
+ hasCancelButtonBeenClicked,
+ ) { selectedUserChanged, screenTurnedOff, cancelButtonClicked ->
+ selectedUserChanged || screenTurnedOff || cancelButtonClicked
+ }
+ }
+
+ private fun toViewModel(
+ model: UserModel,
+ ): UserViewModel {
+ return UserViewModel(
+ viewKey = model.id,
+ name = model.name,
+ image = model.image,
+ isSelectionMarkerVisible = model.isSelected,
+ alpha =
+ if (model.isSelectable) {
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA
+ } else {
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA
+ },
+ onClicked = createOnSelectedCallback(model),
+ )
+ }
+
+ private fun toViewModel(
+ model: UserActionModel,
+ ): UserActionViewModel {
+ return UserActionViewModel(
+ viewKey = model.ordinal.toLong(),
+ iconResourceId =
+ if (model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
+ R.drawable.ic_manage_users
+ } else {
+ LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
+ isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
+ isAddUser = model == UserActionModel.ADD_USER,
+ isGuest = model == UserActionModel.ENTER_GUEST_MODE,
+ )
+ },
+ textResourceId =
+ if (model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
+ R.string.manage_users
+ } else {
+ LegacyUserUiHelper.getUserSwitcherActionTextResourceId(
+ isGuest = model == UserActionModel.ENTER_GUEST_MODE,
+ isGuestUserAutoCreated = userInteractor.isGuestUserAutoCreated,
+ isGuestUserResetting = userInteractor.isGuestUserResetting,
+ isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
+ isAddUser = model == UserActionModel.ADD_USER,
+ )
+ },
+ onClicked = { userInteractor.executeAction(action = model) },
+ )
+ }
+
+ private fun createOnSelectedCallback(model: UserModel): (() -> Unit)? {
+ return if (!model.isSelectable) {
+ null
+ } else {
+ { userInteractor.selectUser(model.id) }
+ }
+ }
+
+ class Factory
+ @Inject
+ constructor(
+ private val userInteractor: UserInteractor,
+ private val powerInteractor: PowerInteractor,
+ ) : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST")
+ return UserSwitcherViewModel(
+ userInteractor = userInteractor,
+ powerInteractor = powerInteractor,
+ )
+ as T
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserViewModel.kt
new file mode 100644
index 0000000..d57bba0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserViewModel.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.user.ui.viewmodel
+
+import android.graphics.drawable.Drawable
+import com.android.systemui.common.shared.model.Text
+
+/** Models UI state for representing a single user. */
+data class UserViewModel(
+ /**
+ * Key to use with the view or compose system to keep track of the view/composable across
+ * changes to the collection of [UserViewModel] instances.
+ */
+ val viewKey: Int,
+ val name: Text,
+ val image: Drawable,
+ /** Whether a marker should be shown to highlight that this user is the selected one. */
+ val isSelectionMarkerVisible: Boolean,
+ val alpha: Float,
+ val onClicked: (() -> Unit)?,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index 7baebf4..729fdfe 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -74,10 +74,19 @@
/**
* Returns a new [Flow] that combines the [Set] changes between each emission from [this] using
* [transform].
+ *
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
+ * a change event to be emitted that contains no removals, and all elements from that first [Set]
+ * as additions.
+ *
+ * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
+ * until a second [Set] has been emitted from the upstream [Flow].
*/
fun <T, R> Flow<Set<T>>.setChangesBy(
transform: suspend (removed: Set<T>, added: Set<T>) -> R,
-): Flow<R> = onStart { emit(emptySet()) }.distinctUntilChanged()
+ emitFirstEvent: Boolean = true,
+): Flow<R> = (if (emitFirstEvent) onStart { emit(emptySet()) } else this)
+ .distinctUntilChanged()
.pairwiseBy { old: Set<T>, new: Set<T> ->
// If an element was present in the old set, but not the new one, then it was removed
val removed = old - new
@@ -86,8 +95,18 @@
transform(removed, added)
}
-/** Returns a new [Flow] that produces the [Set] changes between each emission from [this]. */
-fun <T> Flow<Set<T>>.setChanges(): Flow<SetChanges<T>> = setChangesBy(::SetChanges)
+/**
+ * Returns a new [Flow] that produces the [Set] changes between each emission from [this].
+ *
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
+ * a change event to be emitted that contains no removals, and all elements from that first [Set]
+ * as additions.
+ *
+ * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
+ * until a second [Set] has been emitted from the upstream [Flow].
+ */
+fun <T> Flow<Set<T>>.setChanges(emitFirstEvent: Boolean = true): Flow<SetChanges<T>> =
+ setChangesBy(::SetChanges, emitFirstEvent)
/** Contains the difference in elements between two [Set]s. */
data class SetChanges<T>(
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index e910f72..619e50b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -58,7 +59,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
@@ -304,7 +304,7 @@
MemoryIconDrawable(Context context) {
baseIcon = context.getDrawable(R.drawable.ic_memory).mutate();
dp = context.getResources().getDisplayMetrics().density;
- paint.setColor(QSIconViewImpl.getIconColorForState(context, STATE_ACTIVE));
+ paint.setColor(Color.WHITE);
}
public void setRss(long rss) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
index 2c74fb9..87a167b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
@@ -52,6 +52,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.MediaOutputConstants;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.util.ArrayList;
@@ -69,6 +70,7 @@
private static final int DURATION_SLICE_BINDING_TIMEOUT_MS = 200;
private static final int DEFAULT_SLICE_SIZE = 4;
+ private final ActivityStarter mActivityStarter;
private RecyclerView mVolumePanelSlices;
private VolumePanelSlicesAdapter mVolumePanelSlicesAdapter;
private final LifecycleRegistry mLifecycleRegistry;
@@ -78,8 +80,10 @@
private boolean mSlicesReadyToLoad;
private LocalBluetoothProfileManager mProfileManager;
- public VolumePanelDialog(Context context, boolean aboveStatusBar) {
+ public VolumePanelDialog(Context context,
+ ActivityStarter activityStarter, boolean aboveStatusBar) {
super(context);
+ mActivityStarter = activityStarter;
mLifecycleRegistry = new LifecycleRegistry(this);
if (!aboveStatusBar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
@@ -100,9 +104,11 @@
doneButton.setOnClickListener(v -> dismiss());
Button settingsButton = dialogView.findViewById(R.id.settings_button);
settingsButton.setOnClickListener(v -> {
- getContext().startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS).addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK));
dismiss();
+
+ Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mActivityStarter.startActivity(intent, /* dismissShade= */ true);
});
LocalBluetoothManager localBluetoothManager = LocalBluetoothManager.getInstance(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
index c2fafbf..0debe0e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
@@ -21,6 +21,7 @@
import android.view.View
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
private const val TAG = "VolumePanelFactory"
@@ -33,6 +34,7 @@
@SysUISingleton
class VolumePanelFactory @Inject constructor(
private val context: Context,
+ private val activityStarter: ActivityStarter,
private val dialogLaunchAnimator: DialogLaunchAnimator
) {
companion object {
@@ -45,7 +47,7 @@
return
}
- val dialog = VolumePanelDialog(context, aboveStatusBar)
+ val dialog = VolumePanelDialog(context, activityStarter, aboveStatusBar)
volumePanelDialog = dialog
// Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index ce96741..8c41374 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -57,11 +57,9 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -460,11 +458,6 @@
mBubbles.onNotificationChannelModified(pkg, user, channel, modificationType);
}
- /**
- * Gets the DismissedByUserStats used by {@link NotificationEntryManager}.
- * Will not be necessary when using the new notification pipeline's {@link NotifCollection}.
- * Instead, this is taken care of by {@link BubbleCoordinator}.
- */
private DismissedByUserStats getDismissedByUserStats(
NotificationEntry entry,
boolean isVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index a4a59fc..3961a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -34,7 +34,6 @@
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
@@ -62,12 +61,10 @@
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.protolog.ShellProtoLogImpl;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.sysui.ShellInterface;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -336,44 +333,7 @@
if (mShell.handleCommand(args, pw)) {
return;
}
- // Handle logging commands if provided
- if (handleLoggingCommand(args, pw)) {
- return;
- }
// Dump WMShell stuff here if no commands were handled
mShell.dump(pw);
}
-
- @Override
- public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {
- PrintWriter pw = new PrintWriter(new ParcelFileDescriptor.AutoCloseOutputStream(outFd));
- handleLoggingCommand(args, pw);
- pw.flush();
- pw.close();
- }
-
- private boolean handleLoggingCommand(String[] args, PrintWriter pw) {
- ShellProtoLogImpl protoLogImpl = ShellProtoLogImpl.getSingleInstance();
- for (int i = 0; i < args.length; i++) {
- switch (args[i]) {
- case "enable-text": {
- String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
- int result = protoLogImpl.startTextLogging(groups, pw);
- if (result == 0) {
- pw.println("Starting logging on groups: " + Arrays.toString(groups));
- }
- return true;
- }
- case "disable-text": {
- String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
- int result = protoLogImpl.stopTextLogging(groups, pw);
- if (result == 0) {
- pw.println("Stopping logging on groups: " + Arrays.toString(groups));
- }
- return true;
- }
- }
- }
- return false;
- }
}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index ba28045..0fa3d36 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -53,7 +53,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" />
- <application android:debuggable="true" android:largeHeap="true"
+ <application android:debuggable="true" android:largeHeap="true" android:testOnly="true"
android:enableOnBackInvokedCallback="true" >
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/AndroidTest.xml b/packages/SystemUI/tests/AndroidTest.xml
index 5ffd300..31e7bd2 100644
--- a/packages/SystemUI/tests/AndroidTest.xml
+++ b/packages/SystemUI/tests/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Runs Tests for SystemUI.">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="SystemUITests.apk" />
+ <option name="install-arg" value="-t" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 0ac4eda..914d945 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -259,6 +259,7 @@
@Test
fun unregisterListeners_validate() {
+ clockEventController.clock = clock
clockEventController.unregisterListeners()
verify(broadcastDispatcher).unregisterReceiver(any())
verify(configurationController).removeCallback(any())
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt
new file mode 100644
index 0000000..68d0f41
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.keyguard
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert
+import kotlin.reflect.full.declaredMembers
+import org.junit.Test
+
+@SmallTest
+class FaceAuthReasonTest : SysuiTestCase() {
+ @Test
+ fun testApiReasonToUiEvent_forAllReasons_isNotNull() {
+ val declaredMemberProperties = FaceAuthApiRequestReason.Companion::class.declaredMembers
+
+ declaredMemberProperties.forEach {
+ val reason = it.call()
+ try {
+ apiRequestReasonToUiEvent(reason as String)
+ } catch (e: Exception) {
+ Assert.fail(
+ "Expected the reason: \"$reason\" to have a corresponding FaceAuthUiEvent"
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index bb455da..0bf038d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -86,6 +86,7 @@
becauseCannotSkipBouncer = false,
biometricSettingEnabledForUser = false,
bouncerFullyShown = false,
+ bouncerIsOrWillShow = false,
onlyFaceEnrolled = false,
faceAuthenticated = false,
faceDisabled = false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 3e00aec..fde288f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
@@ -86,6 +87,8 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.ILockSettings;
@@ -96,6 +99,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -190,6 +194,10 @@
private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger;
@Mock
private IActivityManager mActivityService;
+ @Mock
+ private SessionTracker mSessionTracker;
+ @Mock
+ private UiEventLogger mUiEventLogger;
private final int mCurrentUserId = 100;
private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
@@ -212,6 +220,7 @@
private MockitoSession mMockitoSession;
private StatusBarStateController.StateListener mStatusBarStateListener;
private IBiometricEnabledOnKeyguardCallback mBiometricEnabledOnKeyguardCallback;
+ private final InstanceId mKeyguardInstanceId = InstanceId.fakeInstanceId(999);
@Before
public void setup() throws RemoteException {
@@ -225,6 +234,7 @@
when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFaceManager.getSensorPropertiesInternal()).thenReturn(mFaceSensorProperties);
+ when(mSessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(mKeyguardInstanceId);
// IBiometricsFace@1.0 does not support detection, only authentication.
when(mFaceSensorProperties.isEmpty()).thenReturn(false);
@@ -656,7 +666,8 @@
// Stop scanning when bouncer becomes visible
setKeyguardBouncerVisibility(true);
clearInvocations(mFaceManager);
- mKeyguardUpdateMonitor.requestFaceAuth(true);
+ mKeyguardUpdateMonitor.requestFaceAuth(true,
+ FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyBoolean());
}
@@ -1500,10 +1511,13 @@
keyguardIsVisible();
mTestableLooper.processAllMessages();
+ clearInvocations(mUiEventLogger);
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
- mKeyguardUpdateMonitor.requestFaceAuth(true);
+ mKeyguardUpdateMonitor.requestFaceAuth(true,
+ FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
+
verify(mFaceManager).authenticate(any(),
mCancellationSignalCaptor.capture(),
mAuthenticationCallbackCaptor.capture(),
@@ -1512,7 +1526,7 @@
anyBoolean());
CancellationSignal cancelSignal = mCancellationSignalCaptor.getValue();
- bouncerFullyVisible();
+ bouncerWillBeVisibleSoon();
mTestableLooper.processAllMessages();
assertThat(cancelSignal.isCanceled()).isTrue();
@@ -1595,7 +1609,7 @@
}
private void triggerSuccessfulFaceAuth() {
- mKeyguardUpdateMonitor.requestFaceAuth(true);
+ mKeyguardUpdateMonitor.requestFaceAuth(true, FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
verify(mFaceManager).authenticate(any(),
any(),
mAuthenticationCallbackCaptor.capture(),
@@ -1670,6 +1684,11 @@
setKeyguardBouncerVisibility(true);
}
+ private void bouncerWillBeVisibleSoon() {
+ mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true, false);
+ mTestableLooper.processAllMessages();
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();
@@ -1710,7 +1729,7 @@
mStatusBarStateController, mLockPatternUtils,
mAuthController, mTelephonyListenerManager,
mInteractionJankMonitor, mLatencyTracker, mActiveUnlockConfig,
- mKeyguardUpdateMonitorLogger);
+ mKeyguardUpdateMonitorLogger, mUiEventLogger, () -> mSessionTracker);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index ba21afd..b47b08c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -44,12 +44,10 @@
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import org.junit.Before;
import org.junit.Test;
@@ -64,9 +62,7 @@
public class ForegroundServiceControllerTest extends SysuiTestCase {
private ForegroundServiceController mFsc;
private ForegroundServiceNotificationListener mListener;
- private NotificationEntryListener mEntryListener;
- private final FakeSystemClock mClock = new FakeSystemClock();
- @Mock private NotificationEntryManager mEntryManager;
+ private NotifCollectionListener mCollectionListener;
@Mock private AppOpsController mAppOpsController;
@Mock private Handler mMainHandler;
@Mock private NotifPipeline mNotifPipeline;
@@ -79,12 +75,13 @@
MockitoAnnotations.initMocks(this);
mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler);
mListener = new ForegroundServiceNotificationListener(
- mContext, mFsc, mEntryManager, mNotifPipeline, mClock);
- ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
- ArgumentCaptor.forClass(NotificationEntryListener.class);
- verify(mEntryManager).addNotificationEntryListener(
+ mContext, mFsc, mNotifPipeline);
+ mListener.init();
+ ArgumentCaptor<NotifCollectionListener> entryListenerCaptor =
+ ArgumentCaptor.forClass(NotifCollectionListener.class);
+ verify(mNotifPipeline).addCollectionListener(
entryListenerCaptor.capture());
- mEntryListener = entryListenerCaptor.getValue();
+ mCollectionListener = entryListenerCaptor.getValue();
}
@Test
@@ -449,7 +446,7 @@
private NotificationEntry addFgEntry() {
NotificationEntry entry = createFgEntry();
- mEntryListener.onPendingEntryAdded(entry);
+ mCollectionListener.onEntryAdded(entry);
return entry;
}
@@ -461,12 +458,10 @@
}
private void entryRemoved(StatusBarNotification notification) {
- mEntryListener.onEntryRemoved(
+ mCollectionListener.onEntryRemoved(
new NotificationEntryBuilder()
.setSbn(notification)
.build(),
- null,
- false,
REASON_APP_CANCEL);
}
@@ -475,7 +470,7 @@
.setSbn(notification)
.setImportance(importance)
.build();
- mEntryListener.onPendingEntryAdded(entry);
+ mCollectionListener.onEntryAdded(entry);
}
private void entryUpdated(StatusBarNotification notification, int importance) {
@@ -483,7 +478,7 @@
.setSbn(notification)
.setImportance(importance)
.build();
- mEntryListener.onPreEntryUpdated(entry);
+ mCollectionListener.onEntryUpdated(entry);
}
@UserIdInt private static final int USERID_ONE = 10; // UserManagerService.MIN_USER_ID;
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 ce49ced..c0acc71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -90,6 +90,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
@@ -178,6 +179,9 @@
private DelayableExecutor mBackgroundExecutor;
private TestableAuthController mAuthController;
+ @Mock
+ private VibratorHelper mVibratorHelper;
+
@Before
public void setup() throws RemoteException {
mContextSpy = spy(mContext);
@@ -236,10 +240,12 @@
true /* supportsSelfIllumination */,
true /* resetLockoutRequireHardwareAuthToken */));
when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps);
+ when(mVibratorHelper.hasVibrator()).thenReturn(true);
mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
- () -> mUdfpsController, () -> mSidefpsController, mStatusBarStateController);
+ () -> mUdfpsController, () -> mSidefpsController, mStatusBarStateController,
+ mVibratorHelper);
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -268,11 +274,13 @@
reset(mFingerprintManager);
reset(mFaceManager);
+ when(mVibratorHelper.hasVibrator()).thenReturn(true);
+
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
- mStatusBarStateController);
+ mStatusBarStateController, mVibratorHelper);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -298,11 +306,13 @@
reset(mFingerprintManager);
reset(mFaceManager);
+ when(mVibratorHelper.hasVibrator()).thenReturn(true);
+
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
- mStatusBarStateController);
+ mStatusBarStateController, mVibratorHelper);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -963,12 +973,13 @@
FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
Provider<SidefpsController> sidefpsControllerFactory,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ VibratorHelper vibratorHelper) {
super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory,
sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
mUserManager, mLockPatternUtils, statusBarStateController,
- mInteractionJankMonitor, mHandler, mBackgroundExecutor);
+ mInteractionJankMonitor, mHandler, mBackgroundExecutor, vibratorHelper);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
new file mode 100644
index 0000000..419fedf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.biometrics
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BiometricMessageDeferralTest : SysuiTestCase() {
+
+ @Test
+ fun testProcessNoMessages_noDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf())
+
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testProcessNonDeferredMessages_noDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // WHEN there are no deferred messages processed
+ for (i in 0..3) {
+ biometricMessageDeferral.processMessage(4, "test")
+ }
+
+ // THEN getDeferredMessage is null
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testAllProcessedMessagesWereDeferred() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
+
+ // WHEN all the processed messages are a deferred message
+ for (i in 0..3) {
+ biometricMessageDeferral.processMessage(1, "test")
+ }
+
+ // THEN deferredMessage will return the string associated with the deferred msgId
+ assertEquals("test", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testReturnsMostFrequentDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // WHEN there's two msgId=1 processed and one msgId=2 processed
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(2, "msgId-2")
+
+ // THEN the most frequent deferred message is that meets the threshold is returned
+ assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testDeferredMessage_mustMeetThreshold() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
+
+ // WHEN more nonDeferredMessages are shown than the deferred message
+ val totalMessages = 10
+ val nonDeferredMessagesCount =
+ (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
+ for (i in 0 until nonDeferredMessagesCount) {
+ biometricMessageDeferral.processMessage(4, "non-deferred-msg")
+ }
+ for (i in nonDeferredMessagesCount until totalMessages) {
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ }
+
+ // THEN there's no deferred message because it didn't meet the threshold
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testDeferredMessage_manyExcludedMessages_getDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1))
+
+ // WHEN more excludedMessages are shown than the deferred message
+ val totalMessages = 10
+ val excludedMessagesCount = (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
+ for (i in 0 until excludedMessagesCount) {
+ biometricMessageDeferral.processMessage(3, "excluded-msg")
+ }
+ for (i in excludedMessagesCount until totalMessages) {
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ }
+
+ // THEN there IS a deferred message because the deferred msg meets the threshold amongst the
+ // non-excluded messages
+ assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testResetClearsOutCounts() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // GIVEN two msgId=1 events processed
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+
+ // WHEN counts are reset and then a single deferred message is processed (msgId=2)
+ biometricMessageDeferral.reset()
+ biometricMessageDeferral.processMessage(2, "msgId-2")
+
+ // THEN msgId-2 is the deferred message since the two msgId=1 events were reset
+ assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testShouldDefer() {
+ // GIVEN should defer msgIds 1 and 2
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1, 2))
+
+ // THEN shouldDefer returns true for ids 1 & 2
+ assertTrue(biometricMessageDeferral.shouldDefer(1))
+ assertTrue(biometricMessageDeferral.shouldDefer(2))
+
+ // THEN should defer returns false for ids 3 & 4
+ assertFalse(biometricMessageDeferral.shouldDefer(3))
+ assertFalse(biometricMessageDeferral.shouldDefer(4))
+ }
+}
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 a4d2238..eecbee5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -27,11 +27,11 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor.forClass
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -218,4 +218,18 @@
assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
}
-}
\ No newline at end of file
+
+ @Test
+ fun playCannedUnlockAnimation_nullSmartspaceView_doesNotThrowExecption() {
+ keyguardUnlockAnimationController.lockscreenSmartspace = null
+ keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true
+
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index 219b3c8..1bc1972 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -28,9 +28,11 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import javax.inject.Provider
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
import org.junit.Before
@@ -38,8 +40,9 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-import javax.inject.Provider
private val DATA = MediaTestUtils.emptyMediaData
@@ -64,6 +67,10 @@
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var logger: MediaUiEventLogger
@Mock lateinit var debugLogger: MediaCarouselControllerLogger
+ @Mock lateinit var mediaViewHolder: MediaViewHolder
+ @Mock lateinit var player: TransitionLayout
+ @Mock lateinit var recommendationViewHolder: RecommendationViewHolder
+ @Mock lateinit var recommendations: TransitionLayout
private val clock = FakeSystemClock()
private lateinit var mediaCarouselController: MediaCarouselController
@@ -258,4 +265,44 @@
verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
}
-}
\ No newline at end of file
+
+ @Test
+ fun testSetSquishinessFractionForMedia_setPlayerBottom() {
+ whenever(panel.mediaViewHolder).thenReturn(mediaViewHolder)
+ whenever(mediaViewHolder.player).thenReturn(player)
+ whenever(player.measuredHeight).thenReturn(100)
+
+ val playingLocal = Triple("playing local",
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
+ 4500L)
+ MediaPlayerData.addMediaPlayer(playingLocal.first, playingLocal.second, panel, clock,
+ false, debugLogger)
+
+ mediaCarouselController.squishinessFraction = 0.0f
+ verify(player).bottom = 50
+ verifyNoMoreInteractions(recommendationViewHolder)
+
+ mediaCarouselController.squishinessFraction = 0.5f
+ verify(player).bottom = 75
+ verifyNoMoreInteractions(recommendationViewHolder)
+ }
+
+ @Test
+ fun testSetSquishinessFractionForRecommendation_setPlayerBottom() {
+ whenever(panel.recommendationViewHolder).thenReturn(recommendationViewHolder)
+ whenever(recommendationViewHolder.recommendations).thenReturn(recommendations)
+ whenever(recommendations.measuredHeight).thenReturn(100)
+
+ MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, EMPTY_SMARTSPACE_MEDIA_DATA, panel,
+ false, clock)
+
+ mediaCarouselController.squishinessFraction = 0.0f
+ verifyNoMoreInteractions(mediaViewHolder)
+ verify(recommendationViewHolder.recommendations).bottom = 50
+
+ mediaCarouselController.squishinessFraction = 0.5f
+ verifyNoMoreInteractions(mediaViewHolder)
+ verify(recommendationViewHolder.recommendations).bottom = 75
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
new file mode 100644
index 0000000..15465f4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.power.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakePowerRepository(
+ initialInteractive: Boolean = true,
+) : PowerRepository {
+
+ private val _isInteractive = MutableStateFlow(initialInteractive)
+ override val isInteractive: Flow<Boolean> = _isInteractive.asStateFlow()
+
+ fun setInteractive(value: Boolean) {
+ _isInteractive.value = value
+ }
+}
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
new file mode 100644
index 0000000..249a91b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.power.data.repository
+
+import android.content.BroadcastReceiver
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.PowerManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class PowerRepositoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var manager: PowerManager
+ @Mock private lateinit var dispatcher: BroadcastDispatcher
+ @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
+ @Captor private lateinit var filterCaptor: ArgumentCaptor<IntentFilter>
+
+ private lateinit var underTest: PowerRepositoryImpl
+
+ private var isInteractive = true
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ isInteractive = true
+ whenever(manager.isInteractive).then { isInteractive }
+
+ underTest = PowerRepositoryImpl(manager = manager, dispatcher = dispatcher)
+ }
+
+ @Test
+ fun `isInteractive - registers for broadcasts`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isInteractive.onEach {}.launchIn(this)
+
+ verifyRegistered()
+ assertThat(filterCaptor.value.hasAction(Intent.ACTION_SCREEN_ON)).isTrue()
+ assertThat(filterCaptor.value.hasAction(Intent.ACTION_SCREEN_OFF)).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - unregisters from broadcasts`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isInteractive.onEach {}.launchIn(this)
+ verifyRegistered()
+
+ job.cancel()
+
+ verify(dispatcher).unregisterReceiver(receiverCaptor.value)
+ }
+
+ @Test
+ fun `isInteractive - emits initial true value if screen was on`() =
+ runBlocking(IMMEDIATE) {
+ isInteractive = true
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+
+ verifyRegistered()
+
+ assertThat(value).isTrue()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - emits initial false value if screen was off`() =
+ runBlocking(IMMEDIATE) {
+ isInteractive = false
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+
+ verifyRegistered()
+
+ assertThat(value).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - emits true when the screen turns on`() =
+ runBlocking(IMMEDIATE) {
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+ verifyRegistered()
+
+ isInteractive = true
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_ON))
+
+ assertThat(value).isTrue()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - emits false when the screen turns off`() =
+ runBlocking(IMMEDIATE) {
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+ verifyRegistered()
+
+ isInteractive = false
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
+
+ assertThat(value).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - emits correctly over time`() =
+ runBlocking(IMMEDIATE) {
+ val values = mutableListOf<Boolean>()
+ val job = underTest.isInteractive.onEach(values::add).launchIn(this)
+ verifyRegistered()
+
+ isInteractive = false
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
+ isInteractive = true
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_ON))
+ isInteractive = false
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
+
+ assertThat(values).isEqualTo(listOf(true, false, true, false))
+ job.cancel()
+ }
+
+ 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
+ // able to remove this.
+ verify(dispatcher)
+ .registerReceiver(
+ capture(receiverCaptor),
+ capture(filterCaptor),
+ isNull(),
+ isNull(),
+ anyInt(),
+ isNull(),
+ )
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
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
new file mode 100644
index 0000000..bf6a37e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.power.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class PowerInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: PowerInteractor
+ private lateinit var repository: FakePowerRepository
+
+ @Before
+ fun setUp() {
+ repository =
+ FakePowerRepository(
+ initialInteractive = true,
+ )
+ underTest = PowerInteractor(repository = repository)
+ }
+
+ @Test
+ fun `isInteractive - screen turns off`() =
+ runBlocking(IMMEDIATE) {
+ repository.setInteractive(true)
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+
+ repository.setInteractive(false)
+
+ assertThat(value).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - becomes interactive`() =
+ runBlocking(IMMEDIATE) {
+ repository.setInteractive(false)
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+
+ repository.setInteractive(true)
+
+ assertThat(value).isTrue()
+ job.cancel()
+ }
+
+ 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 e2c6ff9..aacc695 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
@@ -21,12 +21,12 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.LogcatEchoTracker
-import com.android.systemui.statusbar.DisableFlagsLogger
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.mockito.Mockito.mock
import java.io.PrintWriter
import java.io.StringWriter
+import org.junit.Test
+import org.mockito.Mockito.mock
@SmallTest
class QSFragmentDisableFlagsLoggerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index c127a6b..ecc8457 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -44,6 +44,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -86,6 +87,7 @@
@Mock
private QSLogger mQSLogger;
private DumpManager mDumpManager = new DumpManager();
+ private MediaCarouselController mMediaCarouselController;
@Mock
QSTileImpl mQSTile;
@Mock
@@ -108,9 +110,9 @@
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
QSCustomizerController qsCustomizerController, MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager) {
+ DumpManager dumpManager, MediaCarouselController mediaCarouselController) {
super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
- qsLogger, dumpManager);
+ qsLogger, dumpManager, mediaCarouselController);
}
@Override
@@ -144,7 +146,7 @@
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
mController.init();
reset(mQSTileRevealController);
@@ -156,7 +158,7 @@
QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
mQSTileHost, mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController) {
@Override
protected QSTileRevealController createTileRevealController() {
return mQSTileRevealController;
@@ -249,7 +251,7 @@
when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape");
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
mController.init();
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
@@ -258,7 +260,7 @@
when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait");
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
mController.init();
assertThat(mController.shouldUseHorizontalLayout()).isFalse();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index c0944ef..98d499a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -6,6 +6,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.MediaCarouselController
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.FalsingManager
@@ -27,8 +28,8 @@
import org.mockito.Mockito.any
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -40,6 +41,7 @@
@Mock private lateinit var qsCustomizerController: QSCustomizerController
@Mock private lateinit var qsTileRevealControllerFactory: QSTileRevealController.Factory
@Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var mediaCarouselController: MediaCarouselController
@Mock private lateinit var metricsLogger: MetricsLogger
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var qsLogger: QSLogger
@@ -76,6 +78,7 @@
mediaHost,
qsTileRevealControllerFactory,
dumpManager,
+ mediaCarouselController,
metricsLogger,
uiEventLogger,
qsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index e4f47fd..39f27d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -23,6 +23,7 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.MediaCarouselController
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.qs.QSTile
@@ -59,6 +60,7 @@
@Mock private lateinit var tileLayout: TileLayout
@Mock private lateinit var tileView: QSTileView
@Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
+ @Mock private lateinit var mediaCarouselController: MediaCarouselController
private val uiEventLogger = UiEventLoggerFake()
private val dumpManager = DumpManager()
@@ -88,7 +90,8 @@
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager)
+ dumpManager,
+ mediaCarouselController)
controller.init()
}
@@ -157,7 +160,8 @@
metricsLogger: MetricsLogger,
uiEventLogger: UiEventLoggerFake,
qsLogger: QSLogger,
- dumpManager: DumpManager
+ dumpManager: DumpManager,
+ mediaCarouselController: MediaCarouselController
) :
QuickQSPanelController(
view,
@@ -169,7 +173,8 @@
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager) {
+ dumpManager,
+ mediaCarouselController) {
private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index cb4f08e..eb907bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -192,6 +192,14 @@
verify(view).setIsSingleCarrier(false)
}
+ @Test
+ fun testAlarmIconIgnored() {
+ controller.init()
+
+ verify(iconContainer).addIgnoredSlot(
+ mContext.getString(com.android.internal.R.string.status_bar_alarm_clock))
+ }
+
private fun stubViews() {
`when`(view.findViewById<View>(anyInt())).thenReturn(mockView)
`when`(view.findViewById<QSCarrierGroup>(R.id.carrier_group)).thenReturn(qsCarrierGroup)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index e4751d1..2a4996f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -70,9 +70,13 @@
val underTest = utils.footerActionsViewModel(showPowerButton = false)
val settings = underTest.settings
- assertThat(settings.contentDescription)
- .isEqualTo(ContentDescription.Resource(R.string.accessibility_quick_settings_settings))
- assertThat(settings.icon).isEqualTo(Icon.Resource(R.drawable.ic_settings))
+ assertThat(settings.icon)
+ .isEqualTo(
+ Icon.Resource(
+ R.drawable.ic_settings,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ )
+ )
assertThat(settings.background).isEqualTo(R.drawable.qs_footer_action_circle)
assertThat(settings.iconTint).isNull()
}
@@ -87,11 +91,13 @@
val underTestWithPower = utils.footerActionsViewModel(showPowerButton = true)
val power = underTestWithPower.power
assertThat(power).isNotNull()
- assertThat(power!!.contentDescription)
+ assertThat(power!!.icon)
.isEqualTo(
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ Icon.Resource(
+ android.R.drawable.ic_lock_power_off,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ )
)
- assertThat(power.icon).isEqualTo(Icon.Resource(android.R.drawable.ic_lock_power_off))
assertThat(power.background).isEqualTo(R.drawable.qs_footer_action_circle_color)
assertThat(power.iconTint)
.isEqualTo(
@@ -164,14 +170,13 @@
utils.setUserSwitcherEnabled(settings, true, userId)
val userSwitcher = currentUserSwitcher()
assertThat(userSwitcher).isNotNull()
- assertThat(userSwitcher!!.contentDescription)
- .isEqualTo(ContentDescription.Loaded("Signed in as foo"))
- assertThat(userSwitcher.icon).isEqualTo(Icon.Loaded(picture))
+ assertThat(userSwitcher!!.icon)
+ .isEqualTo(Icon.Loaded(picture, ContentDescription.Loaded("Signed in as foo")))
assertThat(userSwitcher.background).isEqualTo(R.drawable.qs_footer_action_circle)
// Change the current user name.
userSwitcherControllerWrapper.currentUserName = "bar"
- assertThat(currentUserSwitcher()?.contentDescription)
+ assertThat(currentUserSwitcher()?.icon?.contentDescription)
.isEqualTo(ContentDescription.Loaded("Signed in as bar"))
fun iconTint(): Int? = currentUserSwitcher()!!.iconTint
@@ -243,7 +248,7 @@
// Map any SecurityModel into a non-null SecurityButtonConfig.
val buttonConfig =
SecurityButtonConfig(
- icon = Icon.Resource(0),
+ icon = Icon.Resource(res = 0, contentDescription = null),
text = "foo",
isClickable = true,
)
@@ -340,7 +345,7 @@
assertThat(foregroundServices.displayText).isTrue()
securityToConfig = {
SecurityButtonConfig(
- icon = Icon.Resource(0),
+ icon = Icon.Resource(res = 0, contentDescription = null),
text = "foo",
isClickable = true,
)
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 23e5168..2c76be6 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
@@ -97,7 +97,7 @@
ImageView iv = mock(ImageView.class);
State s = new State();
s.state = Tile.STATE_ACTIVE;
- int desiredColor = mIconView.getColor(s.state);
+ int desiredColor = mIconView.getColor(s);
when(iv.isShown()).thenReturn(true);
mIconView.setIcon(iv, s, true);
@@ -109,7 +109,7 @@
ImageView iv = mock(ImageView.class);
State s = new State();
s.state = Tile.STATE_ACTIVE;
- int desiredColor = mIconView.getColor(s.state);
+ int desiredColor = mIconView.getColor(s);
Icon i = mock(Icon.class);
s.icon = i;
when(i.toString()).thenReturn("MOCK ICON");
@@ -124,6 +124,18 @@
assertFalse(mIconView.toString().contains("lastIcon"));
}
+ @Test
+ public void testIconColorDisabledByPolicy_sameAsUnavailable() {
+ State s1 = new State();
+ s1.state = Tile.STATE_INACTIVE;
+ s1.disabledByPolicy = true;
+
+ State s2 = new State();
+ s2.state = Tile.STATE_UNAVAILABLE;
+
+ assertEquals(mIconView.getColor(s1), mIconView.getColor(s2));
+ }
+
private static Drawable.ConstantState fakeConstantState(Drawable otherDrawable) {
return new Drawable.ConstantState() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 9fdc2fd..d3ec1dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -280,6 +280,88 @@
assertThat(info.collectionItemInfo).isNull()
}
+ @Test
+ fun testDisabledByPolicyInactive_usesUnavailableColors() {
+ val stateDisabledByPolicy = QSTile.State()
+ stateDisabledByPolicy.state = Tile.STATE_INACTIVE
+ stateDisabledByPolicy.disabledByPolicy = true
+
+ val stateUnavailable = QSTile.State()
+ stateUnavailable.state = Tile.STATE_UNAVAILABLE
+
+ tileView.changeState(stateDisabledByPolicy)
+ val colorsDisabledByPolicy = tileView.getCurrentColors()
+
+ tileView.changeState(stateUnavailable)
+ val colorsUnavailable = tileView.getCurrentColors()
+
+ assertThat(colorsDisabledByPolicy).containsExactlyElementsIn(colorsUnavailable)
+ }
+
+ @Test
+ fun testDisabledByPolicyActive_usesUnavailableColors() {
+ val stateDisabledByPolicy = QSTile.State()
+ stateDisabledByPolicy.state = Tile.STATE_ACTIVE
+ stateDisabledByPolicy.disabledByPolicy = true
+
+ val stateUnavailable = QSTile.State()
+ stateUnavailable.state = Tile.STATE_UNAVAILABLE
+
+ tileView.changeState(stateDisabledByPolicy)
+ val colorsDisabledByPolicy = tileView.getCurrentColors()
+
+ tileView.changeState(stateUnavailable)
+ val colorsUnavailable = tileView.getCurrentColors()
+
+ assertThat(colorsDisabledByPolicy).containsExactlyElementsIn(colorsUnavailable)
+ }
+
+ @Test
+ fun testDisabledByPolicy_secondaryLabelText() {
+ val testA11yLabel = "TEST_LABEL"
+ context.orCreateTestableResources
+ .addOverride(
+ R.string.accessibility_tile_disabled_by_policy_action_description,
+ testA11yLabel
+ )
+
+ val stateDisabledByPolicy = QSTile.State()
+ stateDisabledByPolicy.state = Tile.STATE_INACTIVE
+ stateDisabledByPolicy.disabledByPolicy = true
+
+ tileView.changeState(stateDisabledByPolicy)
+
+ val info = AccessibilityNodeInfo(tileView)
+ tileView.onInitializeAccessibilityNodeInfo(info)
+ assertThat(
+ info.actionList.find {
+ it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id
+ }?.label
+ ).isEqualTo(testA11yLabel)
+ }
+
+ @Test
+ fun testDisabledByPolicy_unavailableInStateDescription() {
+ val state = QSTile.BooleanState()
+ val spec = "internet"
+ state.spec = spec
+ state.disabledByPolicy = true
+ state.state = Tile.STATE_INACTIVE
+
+ val unavailableString = "${spec}_unavailable"
+ val offString = "${spec}_off"
+ val onString = "${spec}_on"
+
+ context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf(
+ unavailableString,
+ offString,
+ onString
+ ))
+
+ tileView.changeState(state)
+ assertThat(tileView.stateDescription?.contains(unavailableString)).isTrue()
+ }
+
class FakeTileView(
context: Context,
icon: QSIconView,
@@ -289,4 +371,4 @@
handleStateChanged(state)
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index ed98881..f7b9438e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -33,17 +33,18 @@
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -57,13 +58,13 @@
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when`
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.nullable
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
import java.util.Optional
@SmallTest
@@ -93,8 +94,6 @@
private lateinit var serviceInfo: ControlsServiceInfo
@Mock
private lateinit var uiEventLogger: UiEventLogger
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
@Captor
private lateinit var listingCallbackCaptor:
ArgumentCaptor<ControlsListingController.ControlsListingCallback>
@@ -119,7 +118,6 @@
`when`(qsHost.context).thenReturn(spiedContext)
`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
`when`(controlsComponent.isEnabled()).thenReturn(true)
- `when`(keyguardStateController.isUnlocked()).thenReturn(true)
`when`(controlsController.getPreferredStructure())
.thenReturn(StructureInfo(ComponentName("pkg", "cls"), "structure", listOf()))
secureSettings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 1)
@@ -222,12 +220,19 @@
}
@Test
- fun testStateAvailableIfListings() {
+ fun testStateActiveIfListingsHasControlsFavorited() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
capture(listingCallbackCaptor)
)
`when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
+ `when`(controlsController.getPreferredStructure()).thenReturn(
+ StructureInfo(
+ ComponentName("pkg", "cls"),
+ "structure",
+ listOf(ControlInfo("id", "title", "subtitle", 1))
+ )
+ )
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
@@ -236,6 +241,22 @@
}
@Test
+ fun testStateInactiveIfListingsHasNoControlsFavorited() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+ `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
+ `when`(controlsController.getPreferredStructure())
+ .thenReturn(StructureInfo(ComponentName("pkg", "cls"), "structure", listOf()))
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
fun testStateInactiveIfLocked() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
@@ -281,7 +302,14 @@
capture(listingCallbackCaptor)
)
`when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
- `when`(keyguardStateController.isUnlocked).thenReturn(true)
+ `when`(controlsUiController.resolveActivity()).thenReturn(ControlsActivity::class.java)
+ `when`(controlsController.getPreferredStructure()).thenReturn(
+ StructureInfo(
+ ComponentName("pkg", "cls"),
+ "structure",
+ listOf(ControlInfo("id", "title", "subtitle", 1))
+ )
+ )
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
@@ -305,7 +333,14 @@
)
`when`(controlsComponent.getVisibility())
.thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK)
- `when`(keyguardStateController.isUnlocked).thenReturn(false)
+ `when`(controlsUiController.resolveActivity()).thenReturn(ControlsActivity::class.java)
+ `when`(controlsController.getPreferredStructure()).thenReturn(
+ StructureInfo(
+ ComponentName("pkg", "cls"),
+ "structure",
+ listOf(ControlInfo("id", "title", "subtitle", 1))
+ )
+ )
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
@@ -345,8 +380,7 @@
statusBarStateController,
activityStarter,
qsLogger,
- controlsComponent,
- keyguardStateController
+ controlsComponent
).also {
it.initialize()
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
index 5db3b9c..da52a9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
@@ -53,7 +53,6 @@
@Mock private lateinit var mUserDetailItemView: UserDetailItemView
@Mock private lateinit var mOtherView: View
@Mock private lateinit var mInflatedUserDetailItemView: UserDetailItemView
- @Mock private lateinit var mUserInfo: UserInfo
@Mock private lateinit var mLayoutInflater: LayoutInflater
private var falsingManagerFake: FalsingManagerFake = FalsingManagerFake()
private lateinit var adapter: UserDetailView.Adapter
@@ -142,7 +141,7 @@
private fun createUserRecord(current: Boolean, guest: Boolean) =
UserRecord(
- mUserInfo,
+ UserInfo(0 /* id */, "name", 0 /* flags */),
mPicture,
guest,
current,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index 20c6d9a..e85ffb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -607,6 +607,13 @@
verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
}
+ @Test
+ fun alarmIconNotIgnored() {
+ verify(statusIcons, never()).addIgnoredSlot(
+ context.getString(com.android.internal.R.string.status_bar_alarm_clock)
+ )
+ }
+
private fun createWindowInsets(
topCutout: Rect? = Rect()
): WindowInsets {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index eeb61bc..8511443 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -191,4 +191,11 @@
verify(date).setTextAppearance(R.style.TextAppearance_QS_Status)
verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
}
+
+ @Test
+ fun alarmIconIgnored() {
+ verify(statusIcons).addIgnoredSlot(
+ context.getString(com.android.internal.R.string.status_bar_alarm_clock)
+ )
+ }
}
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 e2ce939..f267013 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -124,7 +124,6 @@
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
@@ -229,8 +228,6 @@
@Mock
private DynamicPrivacyController mDynamicPrivacyController;
@Mock
- private NotificationEntryManager mNotificationEntryManager;
- @Mock
private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@Mock
private KeyguardStateController mKeyguardStateController;
@@ -517,7 +514,6 @@
mFeatureFlags,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(),
- mNotificationEntryManager,
mKeyguardStateController,
mStatusBarStateController,
mStatusBarWindowStateController,
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 6d059b1..43fc8983 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -25,7 +25,6 @@
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.lowlightclock.LowLightClockController
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationShadeDepthController
@@ -39,12 +38,10 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
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.ArgumentCaptor
-import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.never
@@ -87,8 +84,6 @@
@Mock
private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
@Mock
- private lateinit var lowLightClockController: LowLightClockController
- @Mock
private lateinit var pulsingGestureListener: PulsingGestureListener
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
@@ -114,7 +109,6 @@
statusBarKeyguardViewManager,
statusBarWindowStateController,
lockIconViewController,
- Optional.of(lowLightClockController),
centralSurfaces,
notificationShadeWindowController,
keyguardUnlockAnimationController,
@@ -252,31 +246,6 @@
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
assertThat(returnVal).isTrue()
}
-
- @Test
- fun testLowLightClockAttachedWhenExpandedStatusBarSetup() {
- verify(lowLightClockController).attachLowLightClockView(ArgumentMatchers.any())
- }
-
- @Test
- fun testLowLightClockShownWhenDozing() {
- underTest.setDozing(true)
- verify(lowLightClockController).showLowLightClock(true)
- }
-
- @Test
- fun testLowLightClockDozeTimeTickCalled() {
- underTest.dozeTimeTick()
- verify(lowLightClockController).dozeTimeTick()
- }
-
- @Test
- fun testLowLightClockHiddenWhenNotDozing() {
- underTest.setDozing(true)
- verify(lowLightClockController).showLowLightClock(true)
- underTest.setDozing(false)
- verify(lowLightClockController).showLowLightClock(false)
- }
}
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 89a2518..001bfee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -38,7 +38,6 @@
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.lowlightclock.LowLightClockController;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -61,8 +60,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -86,7 +83,6 @@
@Mock private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private LockIconViewController mLockIconViewController;
- @Mock private LowLightClockController mLowLightClockController;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private AmbientState mAmbientState;
@Mock private PulsingGestureListener mPulsingGestureListener;
@@ -121,7 +117,6 @@
mStatusBarKeyguardViewManager,
mStatusBarWindowStateController,
mLockIconViewController,
- Optional.of(mLowLightClockController),
mCentralSurfaces,
mNotificationShadeWindowController,
mKeyguardUnlockAnimationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 7869448..2b4a109 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -19,17 +19,31 @@
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.testing.AndroidTestingRunner
+import android.util.TypedValue
import android.view.LayoutInflater
+import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.clocks.DefaultClock.Companion.DOZE_COLOR
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import java.util.Locale
+import java.util.TimeZone
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.notNull
import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
@@ -39,7 +53,8 @@
@JvmField @Rule val mockito = MockitoJUnit.rule()
- @Mock private lateinit var mockClockView: AnimatableClockView
+ @Mock private lateinit var mockSmallClockView: AnimatableClockView
+ @Mock private lateinit var mockLargeClockView: AnimatableClockView
@Mock private lateinit var layoutInflater: LayoutInflater
@Mock private lateinit var mockClockThumbnail: Drawable
@Mock private lateinit var resources: Resources
@@ -47,14 +62,16 @@
@Before
fun setUp() {
- whenever(layoutInflater.inflate(R.layout.clock_default_small, null))
- .thenReturn(mockClockView)
- whenever(layoutInflater.inflate(R.layout.clock_default_large, null))
- .thenReturn(mockClockView)
+ whenever(layoutInflater.inflate(eq(R.layout.clock_default_small), any(), anyBoolean()))
+ .thenReturn(mockSmallClockView)
+ whenever(layoutInflater.inflate(eq(R.layout.clock_default_large), any(), anyBoolean()))
+ .thenReturn(mockLargeClockView)
whenever(resources.getDrawable(R.drawable.clock_default_thumbnail, null))
.thenReturn(mockClockThumbnail)
+ whenever(mockSmallClockView.getLayoutParams()).thenReturn(FrameLayout.LayoutParams(10, 10))
+ whenever(mockLargeClockView.getLayoutParams()).thenReturn(FrameLayout.LayoutParams(10, 10))
- provider = DefaultClockProvider(layoutInflater, resources)
+ provider = DefaultClockProvider(context, layoutInflater, resources)
}
@Test
@@ -71,7 +88,79 @@
// Default clock provider must always provide the default clock
val clock = provider.createClock(DEFAULT_CLOCK_ID)
assertNotNull(clock)
- assertEquals(clock.smallClock, mockClockView)
- assertEquals(clock.largeClock, mockClockView)
+ assertEquals(clock.smallClock, mockSmallClockView)
+ assertEquals(clock.largeClock, mockLargeClockView)
+ }
+
+ @Test
+ fun defaultClock_initialize() {
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ clock.initialize(resources, 0f, 0f)
+
+ verify(mockSmallClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
+ verify(mockLargeClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
+ verify(mockSmallClockView).onTimeZoneChanged(notNull())
+ verify(mockLargeClockView).onTimeZoneChanged(notNull())
+ verify(mockSmallClockView).refreshTime()
+ verify(mockLargeClockView).refreshTime()
+ verify(mockLargeClockView).setLayoutParams(any())
+ }
+
+ @Test
+ fun defaultClock_events_onTimeTick() {
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ clock.events.onTimeTick()
+
+ verify(mockSmallClockView).refreshTime()
+ verify(mockLargeClockView).refreshTime()
+ }
+
+ @Test
+ fun defaultClock_events_onTimeFormatChanged() {
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ clock.events.onTimeFormatChanged(true)
+
+ verify(mockSmallClockView).refreshFormat(true)
+ verify(mockLargeClockView).refreshFormat(true)
+ }
+
+ @Test
+ fun defaultClock_events_onTimeZoneChanged() {
+ val timeZone = mock<TimeZone>()
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ clock.events.onTimeZoneChanged(timeZone)
+
+ verify(mockSmallClockView).onTimeZoneChanged(timeZone)
+ verify(mockLargeClockView).onTimeZoneChanged(timeZone)
+ }
+
+ @Test
+ fun defaultClock_events_onFontSettingChanged() {
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ clock.events.onFontSettingChanged()
+
+ verify(mockSmallClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), anyFloat())
+ verify(mockLargeClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), anyFloat())
+ verify(mockLargeClockView).setLayoutParams(any())
+ }
+
+ @Test
+ fun defaultClock_events_onColorPaletteChanged() {
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ clock.events.onColorPaletteChanged(resources, true, true)
+
+ verify(mockSmallClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
+ verify(mockLargeClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
+ }
+
+ @Test
+ fun defaultClock_events_onLocaleChanged() {
+ val clock = provider.createClock(DEFAULT_CLOCK_ID)
+ clock.events.onLocaleChanged(Locale.getDefault())
+
+ verify(mockSmallClockView, times(2)).setLineSpacingScale(anyFloat())
+ verify(mockLargeClockView, times(2)).setLineSpacingScale(anyFloat())
+ verify(mockSmallClockView, times(2)).refreshFormat()
+ verify(mockLargeClockView, times(2)).refreshFormat()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
new file mode 100644
index 0000000..d925d0a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
@@ -0,0 +1,83 @@
+/*
+ * 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
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.ExpandHelper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class DragDownHelperTest : SysuiTestCase() {
+
+ private lateinit var dragDownHelper: DragDownHelper
+
+ private val collapsedHeight = 300
+ private val falsingManager: FalsingManager = mock()
+ private val falsingCollector: FalsingCollector = mock()
+ private val dragDownloadCallback: LockscreenShadeTransitionController = mock()
+ private val expandableView: ExpandableView = mock()
+ private val expandCallback: ExpandHelper.Callback = mock()
+
+ @Before
+ fun setUp() {
+ whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight)
+
+ dragDownHelper = DragDownHelper(
+ falsingManager,
+ falsingCollector,
+ dragDownloadCallback,
+ mContext
+ ).also {
+ it.expandCallback = expandCallback
+ }
+ }
+
+ @Test
+ fun cancelChildExpansion_updateHeight() {
+ whenever(expandableView.actualHeight).thenReturn(500)
+
+ dragDownHelper.cancelChildExpansion(expandableView, animationDuration = 0)
+
+ verify(expandableView, atLeast(1)).actualHeight = collapsedHeight
+ }
+
+ @Test
+ fun cancelChildExpansion_dontUpdateHeight() {
+ whenever(expandableView.actualHeight).thenReturn(collapsedHeight)
+
+ dragDownHelper.cancelChildExpansion(expandableView, animationDuration = 0)
+
+ verify(expandableView, never()).actualHeight = 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 05692b3..5fea380 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -19,6 +19,7 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
@@ -65,7 +66,6 @@
import android.graphics.Color;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.Looper;
@@ -602,7 +602,7 @@
String message = mContext.getString(R.string.keyguard_unlock);
mController.setVisible(true);
- mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
+ mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
@@ -636,7 +636,7 @@
when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
mController.setVisible(true);
- mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
+ mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
verify(mStatusBarKeyguardViewManager).showBouncerMessage(eq(message), any());
@@ -651,7 +651,7 @@
mController.setVisible(true);
mController.getKeyguardCallback().onBiometricError(
- FaceManager.FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE);
+ FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE);
verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
}
@@ -698,8 +698,7 @@
BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT,
BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH,
BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT
};
for (int msgId : msgIds) {
mKeyguardUpdateMonitorCallback.onBiometricHelp(
@@ -728,8 +727,7 @@
BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT,
BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH,
BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT
};
for (int msgId : msgIds) {
final String numberedHelpString = helpString + msgId;
@@ -743,6 +741,64 @@
}
@Test
+ public void sendTooDarkFaceHelpMessages_onTimeout_noFpEnrolled() {
+ createController();
+
+ // GIVEN fingerprint NOT enrolled
+ when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ 0)).thenReturn(false);
+
+ // WHEN help message received
+ final String helpString = "helpMsg";
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
+ helpString,
+ BiometricSourceType.FACE
+ );
+
+ // THEN help message not shown yet
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+
+ // WHEN face timeout error received
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_TIMEOUT, "face timeout",
+ BiometricSourceType.FACE);
+
+ // THEN the low light message shows with suggestion to swipe up to unlock
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpString);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void sendTooDarkFaceHelpMessages_onTimeout_fingerprintEnrolled() {
+ createController();
+
+ // GIVEN fingerprint enrolled
+ when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ 0)).thenReturn(true);
+
+ // WHEN help message received
+ final String helpString = "helpMsg";
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
+ helpString,
+ BiometricSourceType.FACE
+ );
+
+ // THEN help message not shown yet
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+
+ // WHEN face timeout error received
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_TIMEOUT, "face timeout",
+ BiometricSourceType.FACE);
+
+ // THEN the low light message shows and suggests trying fingerprint
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpString);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
public void updateMonitor_listenerUpdatesIndication() {
createController();
String restingIndication = "Resting indication";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 79b1bb0..eb4cca8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -27,12 +27,9 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
-import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import org.junit.Before;
@@ -53,10 +50,8 @@
public class NonPhoneDependencyTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationListContainer mListContainer;
- @Mock private NotificationEntryListener mEntryListener;
@Mock private RemoteInputController.Delegate mDelegate;
@Mock private NotificationRemoteInputManager.Callback mRemoteInputManagerCallback;
- @Mock private CheckSaveListener mCheckSaveListener;
@Mock private OnSettingsClickListener mOnSettingsClickListener;
@Before
@@ -70,7 +65,6 @@
@Ignore("Causes binder calls which fail")
@Test
public void testNotificationManagementCodeHasNoDependencyOnStatusBarWindowManager() {
- NotificationEntryManager entryManager = Dependency.get(NotificationEntryManager.class);
NotificationGutsManager gutsManager = Dependency.get(NotificationGutsManager.class);
NotificationLogger notificationLogger = Dependency.get(NotificationLogger.class);
NotificationMediaManager mediaManager = Dependency.get(NotificationMediaManager.class);
@@ -78,7 +72,6 @@
Dependency.get(NotificationRemoteInputManager.class);
NotificationLockscreenUserManager lockscreenUserManager =
Dependency.get(NotificationLockscreenUserManager.class);
- entryManager.addNotificationEntryListener(mEntryListener);
gutsManager.setUpWithPresenter(mPresenter, mListContainer,
mOnSettingsClickListener);
notificationLogger.setUpWithContainer(mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 3cc4ee1..853d1df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -21,7 +21,6 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -54,7 +53,6 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -84,8 +82,6 @@
@Mock
private NotificationVisibilityProvider mVisibilityProvider;
@Mock
- private NotificationEntryManager mEntryManager;
- @Mock
private CommonNotifCollection mNotifCollection;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@@ -114,7 +110,6 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
int currentUserId = ActivityManager.getCurrentUser();
mSettings = new FakeSettings();
@@ -149,12 +144,6 @@
}
@Test
- public void testLockScreenShowNotificationsChangeUpdatesNotifications() {
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
- verify(mEntryManager, times(1)).updateNotifications(anyString());
- }
-
- @Test
public void testLockScreenShowNotificationsFalse() {
mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
@@ -297,13 +286,6 @@
}
@Test
- public void testSettingsObserverUpdatesNotifications() {
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- mLockscreenUserManager.getSettingsObserverForTest().onChange(false);
- verify(mEntryManager, times(1)).updateNotifications(anyString());
- }
-
- @Test
public void testActionUserSwitchedCallsOnUserSwitched() {
Intent intent = new Intent()
.setAction(ACTION_USER_SWITCHED)
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 6e29669..5170678 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -23,8 +23,6 @@
import android.app.Notification;
import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
@@ -36,7 +34,6 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
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;
@@ -70,7 +67,6 @@
@Mock private StatusBarStateController mStateController;
@Mock private RemoteInputUriController mRemoteInputUriController;
@Mock private NotificationClickNotifier mClickNotifier;
- @Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
private TestableNotificationRemoteInputManager mRemoteInputManager;
@@ -85,11 +81,8 @@
mLockscreenUserManager,
mSmartReplyController,
mVisibilityProvider,
- mEntryManager,
- mock(RemoteInputNotificationRebuilder.class),
() -> Optional.of(mock(CentralSurfaces.class)),
mStateController,
- Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
mClickNotifier,
mock(ActionClickLogger.class),
@@ -145,11 +138,8 @@
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager notificationEntryManager,
- RemoteInputNotificationRebuilder rebuilder,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
- Handler mainHandler,
RemoteInputUriController remoteInputUriController,
NotificationClickNotifier clickNotifier,
ActionClickLogger actionClickLogger,
@@ -160,7 +150,6 @@
lockscreenUserManager,
smartReplyController,
visibilityProvider,
- notificationEntryManager,
centralSurfacesOptionalLazy,
statusBarStateController,
remoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
new file mode 100644
index 0000000..44cbe51
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class PulseExpansionHandlerTest : SysuiTestCase() {
+
+ private lateinit var pulseExpansionHandler: PulseExpansionHandler
+
+ private val collapsedHeight = 300
+ private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock()
+ private val bypassController: KeyguardBypassController = mock()
+ private val headsUpManager: HeadsUpManagerPhone = mock()
+ private val roundnessManager: NotificationRoundnessManager = mock()
+ private val configurationController: ConfigurationController = mock()
+ private val statusBarStateController: StatusBarStateController = mock()
+ private val falsingManager: FalsingManager = mock()
+ private val lockscreenShadeTransitionController: LockscreenShadeTransitionController = mock()
+ private val falsingCollector: FalsingCollector = mock()
+ private val dumpManager: DumpManager = mock()
+ private val expandableView: ExpandableView = mock()
+
+ @Before
+ fun setUp() {
+ whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight)
+
+ pulseExpansionHandler = PulseExpansionHandler(
+ mContext,
+ wakeUpCoordinator,
+ bypassController,
+ headsUpManager,
+ roundnessManager,
+ configurationController,
+ statusBarStateController,
+ falsingManager,
+ lockscreenShadeTransitionController,
+ falsingCollector,
+ dumpManager
+ )
+ }
+
+ @Test
+ fun resetChild_updateHeight() {
+ whenever(expandableView.actualHeight).thenReturn(500)
+
+ pulseExpansionHandler.reset(expandableView, animationDuration = 0)
+
+ verify(expandableView, atLeast(1)).actualHeight = collapsedHeight
+ }
+
+ @Test
+ fun resetChild_dontUpdateHeight() {
+ whenever(expandableView.actualHeight).thenReturn(collapsedHeight)
+
+ pulseExpansionHandler.reset(expandableView, animationDuration = 0)
+
+ verify(expandableView, never()).actualHeight = anyInt()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt
new file mode 100644
index 0000000..0fdda62
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.connectivity.ui
+
+import android.telephony.SubscriptionInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.connectivity.NetworkController
+import com.android.systemui.statusbar.connectivity.SignalCallback
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class MobileContextProviderTest : SysuiTestCase() {
+ @Mock private lateinit var networkController: NetworkController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var demoModeController: DemoModeController
+
+ private lateinit var provider: MobileContextProvider
+ private lateinit var signalCallback: SignalCallback
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ provider =
+ MobileContextProvider(
+ networkController,
+ dumpManager,
+ demoModeController,
+ )
+
+ signalCallback = withArgCaptor { verify(networkController).addCallback(capture()) }
+ }
+
+ @Test
+ fun test_oneSubscription_contextHasMccMnc() {
+ // GIVEN there is one SubscriptionInfo
+ signalCallback.setSubs(listOf(SUB_1))
+
+ // WHEN we ask for a mobile context
+ val ctx = provider.getMobileContextForSub(SUB_1_ID, context)
+
+ // THEN the configuration of that context reflect this subscription's MCC/MNC override
+ val config = ctx.resources.configuration
+ assertThat(config.mcc).isEqualTo(SUB_1_MCC)
+ assertThat(config.mnc).isEqualTo(SUB_1_MNC)
+ }
+
+ @Test
+ fun test_twoSubscriptions_eachContextReflectsMccMnc() {
+ // GIVEN there are two SubscriptionInfos
+ signalCallback.setSubs(listOf(SUB_1, SUB_2))
+
+ // WHEN we ask for a mobile context for each sub
+ val ctx1 = provider.getMobileContextForSub(SUB_1_ID, context)
+ val ctx2 = provider.getMobileContextForSub(SUB_2_ID, context)
+
+ // THEN the configuration of each context reflect this subscription's MCC/MNC override
+ val config1 = ctx1.resources.configuration
+ assertThat(config1.mcc).isEqualTo(SUB_1_MCC)
+ assertThat(config1.mnc).isEqualTo(SUB_1_MNC)
+
+ val config2 = ctx2.resources.configuration
+ assertThat(config2.mcc).isEqualTo(SUB_2_MCC)
+ assertThat(config2.mnc).isEqualTo(SUB_2_MNC)
+ }
+
+ @Test
+ fun test_requestingContextForNonexistentSubscription_returnsGivenContext() {
+ // GIVEN no SubscriptionInfos
+ signalCallback.setSubs(listOf())
+
+ // WHEN we ask for a mobile context for an unknown subscription
+ val ctx = provider.getMobileContextForSub(SUB_1_ID, context)
+
+ // THEN we get the original context back
+ assertThat(ctx).isEqualTo(context)
+ }
+
+ private val SUB_1_ID = 1
+ private val SUB_1_MCC = 123
+ private val SUB_1_MNC = 456
+ private val SUB_1 =
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_1_ID)
+ whenever(it.mcc).thenReturn(SUB_1_MCC)
+ whenever(it.mnc).thenReturn(SUB_1_MNC)
+ }
+
+ private val SUB_2_ID = 2
+ private val SUB_2_MCC = 666
+ private val SUB_2_MNC = 777
+ private val SUB_2 =
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_2_ID)
+ whenever(it.mcc).thenReturn(SUB_2_MCC)
+ whenever(it.mnc).thenReturn(SUB_2_MNC)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
index c0d1155..4b5d0a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar
+package com.android.systemui.statusbar.disableflags
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -113,7 +113,7 @@
@Test
fun getDisableFlagsString_newAfterLocalModificationSameAsNew_localModNotLogged() {
- val newState = DisableFlagsLogger.DisableState(
+ val newState = DisableFlagsLogger.DisableState(
0b001, // abC
0b10 // mn
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
index d4f0505..8272f5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
@@ -23,11 +23,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
-import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -46,7 +44,6 @@
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
-import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -61,10 +58,6 @@
@Mock
private lateinit var smartspaceController: LockscreenSmartspaceController
@Mock
- private lateinit var notificationEntryManager: NotificationEntryManager
- @Mock
- private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
- @Mock
private lateinit var notifPipeline: NotifPipeline
@Mock
private lateinit var pluggableListener: Pluggable.PluggableListener<NotifFilter>
@@ -99,12 +92,10 @@
deduper = SmartspaceDedupingCoordinator(
statusBarStateController,
smartspaceController,
- notificationEntryManager,
- notificationLockscreenUserManager,
notifPipeline,
executor,
clock
- )
+ )
// Attach the deduper and capture the listeners/filters that it registers
deduper.attach(notifPipeline)
@@ -352,7 +343,6 @@
// THEN the new pipeline is invalidated (but the old one isn't because it's not
// necessary) because the notif should no longer be filtered out
verify(pluggableListener).onPluggableInvalidated(eq(filter), any())
- verify(notificationEntryManager, never()).updateNotifications(anyString())
assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
}
@@ -390,7 +380,6 @@
private fun verifyPipelinesInvalidated() {
verify(pluggableListener).onPluggableInvalidated(eq(filter), any())
- verify(notificationEntryManager).updateNotifications(anyString())
}
private fun assertExecutorIsClear() {
@@ -399,12 +388,10 @@
private fun verifyPipelinesNotInvalidated() {
verify(pluggableListener, never()).onPluggableInvalidated(eq(filter), any())
- verify(notificationEntryManager, never()).updateNotifications(anyString())
}
private fun clearPipelineInvocations() {
clearInvocations(pluggableListener)
- clearInvocations(notificationEntryManager)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 8a7b9d3..b2dc842 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -44,8 +44,6 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifLiveData;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -85,11 +83,9 @@
@Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
// Dependency mocks:
- @Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private NotifLiveDataStore mNotifLiveDataStore;
@Mock private NotifLiveData<List<NotificationEntry>> mActiveNotifEntries;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
- @Mock private NotificationEntryManager mEntryManager;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NotificationListener mListener;
@@ -118,17 +114,14 @@
mLogger = new TestableNotificationLogger(
mListener,
mUiBgExecutor,
- mNotifPipelineFlags,
mNotifLiveDataStore,
mVisibilityProvider,
- mEntryManager,
mNotifPipeline,
mock(StatusBarStateControllerImpl.class),
mBarService,
mExpansionStateLogger
);
mLogger.setUpWithContainer(mListContainer);
- verify(mEntryManager, never()).addNotificationEntryListener(any());
verify(mNotifPipeline).addCollectionListener(any());
}
@@ -266,10 +259,8 @@
TestableNotificationLogger(NotificationListener notificationListener,
Executor uiBgExecutor,
- NotifPipelineFlags notifPipelineFlags,
NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
StatusBarStateControllerImpl statusBarStateController,
IStatusBarService barService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index fe2f5f0..6d687a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -57,7 +57,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -84,8 +83,6 @@
private StatusBarNotification mSbn;
@Mock
- private NotificationEntryManager mNotificationEntryManager;
- @Mock
private IStatusBarService mStatusBarService;
@Mock
private NotificationGutsManager mNotificationGutsManager;
@@ -94,7 +91,6 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
mDependency.injectTestDependency(IStatusBarService.class, mStatusBarService);
mDependency.injectTestDependency(NotificationGutsManager.class, mNotificationGutsManager);
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 966e233..8be9eb5 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
@@ -58,7 +58,6 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -119,7 +118,6 @@
@Mock private SectionHeaderController mSilentHeaderController;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NotifCollection mNotifCollection;
- @Mock private NotificationEntryManager mEntryManager;
@Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@@ -168,7 +166,6 @@
mSilentHeaderController,
mNotifPipeline,
mNotifCollection,
- mEntryManager,
mLockscreenShadeTransitionController,
mShadeTransitionController,
mUiEventLogger,
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 35d2363b..24eff5f 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
@@ -179,7 +179,28 @@
}
@Test
- fun resetViewStates_hiddenShelf_viewAlphaDoesNotChange() {
+ fun resetViewStates_hiddenShelf_allRowsBecomesTransparent() {
+ hostView.removeAllViews()
+ val row1 = mockExpandableNotificationRow()
+ hostView.addView(row1)
+ val row2 = mockExpandableNotificationRow()
+ hostView.addView(row2)
+
+ ambientState.setStatusBarState(StatusBarState.KEYGUARD)
+ ambientState.hideAmount = 0.25f
+ notificationShelf.viewState.hidden = true
+ ambientState.shelf = notificationShelf
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ val expected = 1f - ambientState.hideAmount
+ assertThat(row1.viewState.alpha).isEqualTo(expected)
+ assertThat(row2.viewState.alpha).isEqualTo(expected)
+ }
+
+ @Test
+ fun resetViewStates_hiddenShelf_shelfAlphaDoesNotChange() {
val expected = notificationShelf.viewState.alpha
notificationShelf.viewState.hidden = true
ambientState.shelf = notificationShelf
@@ -459,3 +480,9 @@
assertEquals(1f, currentRoundness)
}
}
+
+private fun mockExpandableNotificationRow(): ExpandableNotificationRow {
+ return mock(ExpandableNotificationRow::class.java).apply {
+ whenever(viewState).thenReturn(ExpandableViewState())
+ }
+}
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 57fb976..e1f20d5 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
@@ -44,8 +44,8 @@
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
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 735a88d..14b7471 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
@@ -127,7 +127,6 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -223,7 +222,6 @@
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private AssistManager mAssistManager;
- @Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
@Mock private NavigationBarController mNavigationBarController;
@@ -397,7 +395,6 @@
new FalsingManagerFake(),
new FalsingCollectorFake(),
mBroadcastDispatcher,
- mNotificationEntryManager,
mNotificationGutsManager,
notificationLogger,
mNotificationInterruptStateProvider,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
new file mode 100644
index 0000000..64dee95
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -0,0 +1,230 @@
+/*
+ * 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.phone
+
+import android.app.AlarmManager
+import android.app.IActivityManager
+import android.app.admin.DevicePolicyManager
+import android.content.SharedPreferences
+import android.os.UserManager
+import android.telecom.TelecomManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.screenrecord.RecordingController
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.policy.BluetoothController
+import com.android.systemui.statusbar.policy.CastController
+import com.android.systemui.statusbar.policy.DataSaverController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HotspotController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.LocationController
+import com.android.systemui.statusbar.policy.NextAlarmController
+import com.android.systemui.statusbar.policy.RotationLockController
+import com.android.systemui.statusbar.policy.SensorPrivacyController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.util.RingerModeTracker
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.DateFormatUtil
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class PhoneStatusBarPolicyTest : SysuiTestCase() {
+
+ companion object {
+ private const val ALARM_SLOT = "alarm"
+ }
+
+ @Mock
+ private lateinit var iconController: StatusBarIconController
+ @Mock
+ private lateinit var commandQueue: CommandQueue
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var castController: CastController
+ @Mock
+ private lateinit var hotspotController: HotspotController
+ @Mock
+ private lateinit var bluetoothController: BluetoothController
+ @Mock
+ private lateinit var nextAlarmController: NextAlarmController
+ @Mock
+ private lateinit var userInfoController: UserInfoController
+ @Mock
+ private lateinit var rotationLockController: RotationLockController
+ @Mock
+ private lateinit var dataSaverController: DataSaverController
+ @Mock
+ private lateinit var zenModeController: ZenModeController
+ @Mock
+ private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var locationController: LocationController
+ @Mock
+ private lateinit var sensorPrivacyController: SensorPrivacyController
+ @Mock
+ private lateinit var iActivityManager: IActivityManager
+ @Mock
+ private lateinit var alarmManager: AlarmManager
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock
+ private lateinit var recordingController: RecordingController
+ @Mock
+ private lateinit var telecomManager: TelecomManager
+ @Mock
+ private lateinit var sharedPreferences: SharedPreferences
+ @Mock
+ private lateinit var dateFormatUtil: DateFormatUtil
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var ringerModeTracker: RingerModeTracker
+ @Mock
+ private lateinit var privacyItemController: PrivacyItemController
+ @Mock
+ private lateinit var privacyLogger: PrivacyLogger
+ @Captor
+ private lateinit var alarmCallbackCaptor:
+ ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback>
+
+ private lateinit var executor: FakeExecutor
+ private lateinit var statusBarPolicy: PhoneStatusBarPolicy
+ private lateinit var testableLooper: TestableLooper
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ executor = FakeExecutor(FakeSystemClock())
+ testableLooper = TestableLooper.get(this)
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.string.status_bar_alarm_clock,
+ ALARM_SLOT
+ )
+ statusBarPolicy = createStatusBarPolicy()
+ }
+
+ @Test
+ fun testDeviceNotProvisioned_alarmIconNotShown() {
+ val alarmInfo = createAlarmInfo()
+
+ whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
+ statusBarPolicy.init()
+ verify(nextAlarmController).addCallback(capture(alarmCallbackCaptor))
+
+ whenever(alarmManager.getNextAlarmClock(anyInt())).thenReturn(alarmInfo)
+
+ alarmCallbackCaptor.value.onNextAlarmChanged(alarmInfo)
+ verify(iconController, never()).setIconVisibility(ALARM_SLOT, true)
+ }
+
+ @Test
+ fun testDeviceProvisioned_alarmIconShown() {
+ val alarmInfo = createAlarmInfo()
+
+ whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
+ statusBarPolicy.init()
+
+ verify(nextAlarmController).addCallback(capture(alarmCallbackCaptor))
+ whenever(alarmManager.getNextAlarmClock(anyInt())).thenReturn(alarmInfo)
+
+ alarmCallbackCaptor.value.onNextAlarmChanged(alarmInfo)
+ verify(iconController).setIconVisibility(ALARM_SLOT, true)
+ }
+
+ @Test
+ fun testDeviceProvisionedChanged_alarmIconShownAfterCurrentUserSetup() {
+ val alarmInfo = createAlarmInfo()
+
+ whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
+ statusBarPolicy.init()
+
+ verify(nextAlarmController).addCallback(capture(alarmCallbackCaptor))
+ whenever(alarmManager.getNextAlarmClock(anyInt())).thenReturn(alarmInfo)
+
+ alarmCallbackCaptor.value.onNextAlarmChanged(alarmInfo)
+ verify(iconController, never()).setIconVisibility(ALARM_SLOT, true)
+
+ whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
+ statusBarPolicy.onUserSetupChanged()
+ verify(iconController).setIconVisibility(ALARM_SLOT, true)
+ }
+
+ private fun createAlarmInfo(): AlarmManager.AlarmClockInfo {
+ return AlarmManager.AlarmClockInfo(10L, null)
+ }
+
+ private fun createStatusBarPolicy(): PhoneStatusBarPolicy {
+ return PhoneStatusBarPolicy(
+ iconController,
+ commandQueue,
+ broadcastDispatcher,
+ executor,
+ testableLooper.looper,
+ context.resources,
+ castController,
+ hotspotController,
+ bluetoothController,
+ nextAlarmController,
+ userInfoController,
+ rotationLockController,
+ dataSaverController,
+ zenModeController,
+ deviceProvisionedController,
+ keyguardStateController,
+ locationController,
+ sensorPrivacyController,
+ iActivityManager,
+ alarmManager,
+ userManager,
+ devicePolicyManager,
+ recordingController,
+ telecomManager,
+ /* displayId = */ 0,
+ sharedPreferences,
+ dateFormatUtil,
+ ringerModeTracker,
+ privacyItemController,
+ privacyLogger
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index ca98143..de7db74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -20,7 +20,10 @@
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -30,12 +33,12 @@
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
@@ -55,15 +58,19 @@
@SmallTest
public class StatusBarIconControllerTest extends LeakCheckedTest {
+ private MobileContextProvider mMobileContextProvider = mock(MobileContextProvider.class);
+
@Before
public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+ // For testing, ignore context overrides
+ when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext);
}
@Test
public void testSetCalledOnAdd_IconManager() {
LinearLayout layout = new LinearLayout(mContext);
- TestIconManager manager = new TestIconManager(layout);
+ TestIconManager manager = new TestIconManager(layout, mMobileContextProvider);
testCallOnAdd_forManager(manager);
}
@@ -72,9 +79,9 @@
LinearLayout layout = new LinearLayout(mContext);
TestDarkIconManager manager = new TestDarkIconManager(
layout,
- mock(FeatureFlags.class),
mock(StatusBarPipelineFlags.class),
() -> mock(WifiViewModel.class),
+ mMobileContextProvider,
mock(DarkIconDispatcher.class));
testCallOnAdd_forManager(manager);
}
@@ -114,11 +121,14 @@
TestDarkIconManager(
LinearLayout group,
- FeatureFlags featureFlags,
StatusBarPipelineFlags statusBarPipelineFlags,
Provider<WifiViewModel> wifiViewModelProvider,
+ MobileContextProvider contextProvider,
DarkIconDispatcher darkIconDispatcher) {
- super(group, featureFlags, statusBarPipelineFlags, wifiViewModelProvider,
+ super(group,
+ statusBarPipelineFlags,
+ wifiViewModelProvider,
+ contextProvider,
darkIconDispatcher);
}
@@ -153,11 +163,11 @@
}
private static class TestIconManager extends IconManager implements TestableIconManager {
- TestIconManager(ViewGroup group) {
+ TestIconManager(ViewGroup group, MobileContextProvider contextProvider) {
super(group,
- mock(FeatureFlags.class),
mock(StatusBarPipelineFlags.class),
- () -> mock(WifiViewModel.class));
+ () -> mock(WifiViewModel.class),
+ contextProvider);
}
@Override
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 2b80508..c409857 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
@@ -66,7 +66,6 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -102,8 +101,6 @@
@Mock
private AssistManager mAssistManager;
@Mock
- private NotificationEntryManager mEntryManager;
- @Mock
private ActivityStarter mActivityStarter;
@Mock
private NotificationClickNotifier mClickNotifier;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 1ec4de9..c3a7e65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -37,7 +37,6 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -55,7 +54,6 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
- @Mock private NotificationEntryManager mEntryManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private com.android.systemui.shade.ShadeController mShadeController;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -71,7 +69,6 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
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 1ee8875..65e2964 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
@@ -21,12 +21,12 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.LogcatEchoTracker
-import com.android.systemui.statusbar.DisableFlagsLogger
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.mockito.Mockito.mock
import java.io.PrintWriter
import java.io.StringWriter
+import org.junit.Test
+import org.mockito.Mockito.mock
@SmallTest
class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index ceaceb4..faadd24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -55,8 +55,8 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.OperatorNameViewController;
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
new file mode 100644
index 0000000..6dbee2f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
@@ -0,0 +1,295 @@
+/*
+ * 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.pipeline.shared.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+class ConnectivityRepositoryImplTest : SysuiTestCase() {
+
+ private lateinit var underTest: ConnectivityRepositoryImpl
+
+ @Mock private lateinit var connectivitySlots: ConnectivitySlots
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var logger: ConnectivityPipelineLogger
+ private lateinit var scope: CoroutineScope
+ @Mock private lateinit var tunerService: TunerService
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ scope = CoroutineScope(IMMEDIATE)
+
+ underTest = ConnectivityRepositoryImpl(
+ connectivitySlots,
+ context,
+ dumpManager,
+ logger,
+ scope,
+ tunerService,
+ )
+ }
+
+ @After
+ fun tearDown() {
+ scope.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+ context.getOrCreateTestableResources().addOverride(
+ DEFAULT_HIDDEN_ICONS_RESOURCE,
+ arrayOf(SLOT_WIFI, SLOT_ETHERNET)
+ )
+ // Re-create our [ConnectivityRepositoryImpl], since it fetches
+ // config_statusBarIconsToExclude when it's first constructed
+ underTest = ConnectivityRepositoryImpl(
+ connectivitySlots,
+ context,
+ dumpManager,
+ logger,
+ scope,
+ tunerService,
+ )
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_slotNamesAdded_flowHasSlots() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+
+ assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_wrongKey_doesNotUpdate() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+
+ // WHEN onTuningChanged with the wrong key
+ getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
+ yield()
+
+ // THEN we didn't update our value and still have the old one
+ assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+ context.getOrCreateTestableResources().addOverride(
+ DEFAULT_HIDDEN_ICONS_RESOURCE,
+ arrayOf(SLOT_WIFI, SLOT_ETHERNET)
+ )
+ // Re-create our [ConnectivityRepositoryImpl], since it fetches
+ // config_statusBarIconsToExclude when it's first constructed
+ underTest = ConnectivityRepositoryImpl(
+ connectivitySlots,
+ context,
+ dumpManager,
+ logger,
+ scope,
+ tunerService,
+ )
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ // First, update the slots
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+ assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+
+ // WHEN we update to a null value
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
+ yield()
+
+ // THEN we go back to our default value
+ assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
+ .thenReturn(ConnectivitySlot.WIFI)
+ whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
+
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
+
+ assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ // WHEN there's empty and blank slot names
+ getTunable().onTuningChanged(
+ HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE, ,,$SLOT_WIFI"
+ )
+
+ // THEN we skip that slot but still process the other ones
+ assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() = runBlocking(IMMEDIATE) {
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
+ whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
+ whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
+
+ getTunable().onTuningChanged(
+ HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
+ )
+
+ assertThat(latest).isEmpty()
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_newSubscriberGetsCurrentValue() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+
+ var latest1: Set<ConnectivitySlot>? = null
+ val job1 = underTest
+ .forceHiddenSlots
+ .onEach { latest1 = it }
+ .launchIn(this)
+
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
+
+ assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
+
+ // WHEN we add a second subscriber after having already emitted a value
+ var latest2: Set<ConnectivitySlot>? = null
+ val job2 = underTest
+ .forceHiddenSlots
+ .onEach { latest2 = it }
+ .launchIn(this)
+
+ // THEN the second subscribe receives the already-emitted value
+ assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
+
+ job1.cancel()
+ job2.cancel()
+ }
+
+ private fun getTunable(): TunerService.Tunable {
+ val callbackCaptor = argumentCaptor<TunerService.Tunable>()
+ Mockito.verify(tunerService).addTunable(callbackCaptor.capture(), any())
+ return callbackCaptor.value!!
+ }
+
+ private fun setUpEthernetWifiMobileSlotNames() {
+ whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET))
+ .thenReturn(ConnectivitySlot.ETHERNET)
+ whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
+ .thenReturn(ConnectivitySlot.WIFI)
+ whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE))
+ .thenReturn(ConnectivitySlot.MOBILE)
+ }
+
+ companion object {
+ private const val SLOT_ETHERNET = "ethernet"
+ private const val SLOT_WIFI = "wifi"
+ private const val SLOT_MOBILE = "mobile"
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
new file mode 100644
index 0000000..bd70034
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.pipeline.shared.data.repository
+
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/** Fake implementation of [ConnectivityRepository] exposing set methods for all the flows. */
+class FakeConnectivityRepository : ConnectivityRepository {
+ private val _forceHiddenIcons: MutableStateFlow<Set<ConnectivitySlot>> =
+ MutableStateFlow(emptySet())
+ override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = _forceHiddenIcons
+
+ fun setForceHiddenIcons(hiddenIcons: Set<ConnectivitySlot>) {
+ _forceHiddenIcons.value = hiddenIcons
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
index 9d8b4bc..e896749 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
@@ -18,6 +18,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
@@ -37,18 +39,22 @@
private lateinit var underTest: WifiInteractor
- private lateinit var repository: FakeWifiRepository
+ private lateinit var connectivityRepository: FakeConnectivityRepository
+ private lateinit var wifiRepository: FakeWifiRepository
@Before
fun setUp() {
- repository = FakeWifiRepository()
- underTest = WifiInteractor(repository)
+ connectivityRepository = FakeConnectivityRepository()
+ wifiRepository = FakeWifiRepository()
+ underTest = WifiInteractor(connectivityRepository, wifiRepository)
}
@Test
fun hasActivityIn_noInOrOut_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+ )
var latest: Boolean? = null
val job = underTest
@@ -63,8 +69,10 @@
@Test
fun hasActivityIn_onlyOut_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -79,8 +87,10 @@
@Test
fun hasActivityIn_onlyIn_outputsTrue() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
var latest: Boolean? = null
val job = underTest
@@ -95,8 +105,10 @@
@Test
fun hasActivityIn_inAndOut_outputsTrue() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -111,8 +123,10 @@
@Test
fun hasActivityIn_ssidNull_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = null))
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = null))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -127,8 +141,10 @@
@Test
fun hasActivityIn_inactiveNetwork_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.Inactive)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -143,8 +159,10 @@
@Test
fun hasActivityIn_carrierMergedNetwork_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -159,7 +177,7 @@
@Test
fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
var latest: Boolean? = null
val job = underTest
@@ -168,23 +186,33 @@
.launchIn(this)
// Conduct a series of changes and verify we catch each of them in succession
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
yield()
assertThat(latest).isTrue()
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
+ )
yield()
assertThat(latest).isFalse()
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
yield()
assertThat(latest).isTrue()
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
yield()
assertThat(latest).isTrue()
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+ )
yield()
assertThat(latest).isFalse()
@@ -200,7 +228,7 @@
ssid = "AB",
passpointProviderFriendlyName = "friendly"
)
- repository.setWifiNetwork(wifiNetwork)
+ wifiRepository.setWifiNetwork(wifiNetwork)
var latest: WifiNetworkModel? = null
val job = underTest
@@ -213,6 +241,36 @@
job.cancel()
}
+ @Test
+ fun isForceHidden_repoHasWifiHidden_outputsTrue() = runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+
+ var latest: Boolean? = null
+ val job = underTest
+ .isForceHidden
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isForceHidden_repoDoesNotHaveWifiHidden_outputsFalse() = runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf())
+
+ var latest: Boolean? = null
+ val job = underTest
+ .isForceHidden
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
companion object {
val VALID_WIFI_NETWORK_MODEL = WifiNetworkModel.Active(networkId = 1, ssid = "AB")
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index f0a775b..6c6d9e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -18,11 +18,14 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
@@ -50,14 +53,16 @@
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var logger: ConnectivityPipelineLogger
@Mock private lateinit var constants: WifiConstants
- private lateinit var repository: FakeWifiRepository
+ private lateinit var connectivityRepository: FakeConnectivityRepository
+ private lateinit var wifiRepository: FakeWifiRepository
private lateinit var interactor: WifiInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- repository = FakeWifiRepository()
- interactor = WifiInteractor(repository)
+ connectivityRepository = FakeConnectivityRepository()
+ wifiRepository = FakeWifiRepository()
+ interactor = WifiInteractor(connectivityRepository, wifiRepository)
underTest = WifiViewModel(
statusBarPipelineFlags,
@@ -68,42 +73,13 @@
}
@Test
- fun wifiIconResId_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.Inactive)
+ fun wifiIcon_forceHidden_outputsNull() = runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
- var latest: Int? = null
+ var latest: Icon? = null
val job = underTest
- .wifiIconResId
- .onEach { latest = it }
- .launchIn(this)
-
- assertThat(latest).isEqualTo(WIFI_NO_NETWORK)
-
- job.cancel()
- }
-
- @Test
- fun wifiIconResId_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
-
- var latest: Int? = null
- val job = underTest
- .wifiIconResId
- .onEach { latest = it }
- .launchIn(this)
-
- assertThat(latest).isNull()
-
- job.cancel()
- }
-
- @Test
- fun wifiIconResId_isActiveNullLevel_outputsNull() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null))
-
- var latest: Int? = null
- val job = underTest
- .wifiIconResId
+ .wifiIcon
.onEach { latest = it }
.launchIn(this)
@@ -113,10 +89,72 @@
}
@Test
- fun wifiIconResId_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) {
+ fun wifiIcon_notForceHidden_outputsVisible() = runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf())
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
+
+ var latest: Icon? = null
+ val job = underTest
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isInstanceOf(Icon.Resource::class.java)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiIcon_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+
+ var latest: Icon? = null
+ val job = underTest
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isInstanceOf(Icon.Resource::class.java)
+ assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_NO_NETWORK)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiIcon_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
+
+ var latest: Icon? = null
+ val job = underTest
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isNull()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiIcon_isActiveNullLevel_outputsNull() = runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null))
+
+ var latest: Icon? = null
+ val job = underTest
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isNull()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiIcon_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) {
val level = 1
- repository.setWifiNetwork(
+ wifiRepository.setWifiNetwork(
WifiNetworkModel.Active(
NETWORK_ID,
isValidated = true,
@@ -124,22 +162,23 @@
)
)
- var latest: Int? = null
+ var latest: Icon? = null
val job = underTest
- .wifiIconResId
- .onEach { latest = it }
- .launchIn(this)
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
- assertThat(latest).isEqualTo(WIFI_FULL_ICONS[level])
+ assertThat(latest).isInstanceOf(Icon.Resource::class.java)
+ assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_FULL_ICONS[level])
job.cancel()
}
@Test
- fun wifiIconResId_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) {
+ fun wifiIcon_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) {
val level = 4
- repository.setWifiNetwork(
+ wifiRepository.setWifiNetwork(
WifiNetworkModel.Active(
NETWORK_ID,
isValidated = false,
@@ -147,13 +186,14 @@
)
)
- var latest: Int? = null
+ var latest: Icon? = null
val job = underTest
- .wifiIconResId
- .onEach { latest = it }
- .launchIn(this)
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
- assertThat(latest).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
+ assertThat(latest).isInstanceOf(Icon.Resource::class.java)
+ assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
job.cancel()
}
@@ -161,7 +201,7 @@
@Test
fun activityInVisible_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(false)
- repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
@@ -178,7 +218,7 @@
@Test
fun activityInVisible_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(false)
- repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
@@ -187,7 +227,9 @@
.launchIn(this)
// Update the repo to have activityIn
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
yield()
// Verify that we didn't update to activityIn=true (because our config is false)
@@ -199,7 +241,7 @@
@Test
fun activityInVisible_showActivityConfigTrue_outputsUpdate() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
- repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
@@ -208,7 +250,9 @@
.launchIn(this)
// Update the repo to have activityIn
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
yield()
// Verify that we updated to activityIn=true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
index c3805ad..8290dab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
@@ -57,8 +57,6 @@
@Mock
private lateinit var inflatedUserDetailItemView: KeyguardUserDetailItemView
@Mock
- private lateinit var userInfo: UserInfo
- @Mock
private lateinit var layoutInflater: LayoutInflater
@Mock
private lateinit var keyguardUserSwitcherController: KeyguardUserSwitcherController
@@ -188,7 +186,7 @@
private fun createUserRecord(isCurrentUser: Boolean, isGuestUser: Boolean) =
UserRecord(
- userInfo,
+ UserInfo(0 /* id */, "name", 0 /* flags */),
picture,
isGuestUser,
isCurrentUser,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
index 66367ec..439beaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
@@ -24,9 +24,11 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -54,6 +56,10 @@
private lateinit var userManager: UserManager
@Mock
private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var flags: FeatureFlags
+ @Mock
+ private lateinit var viewModelFactoryLazy: dagger.Lazy<UserSwitcherViewModel.Factory>
@Before
fun setUp() {
@@ -61,11 +67,12 @@
activity = UserSwitcherActivity(
userSwitcherController,
broadcastDispatcher,
- layoutInflater,
falsingCollector,
falsingManager,
userManager,
- userTracker
+ userTracker,
+ flags,
+ viewModelFactoryLazy,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
new file mode 100644
index 0000000..20f1e36
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.user.data.repository
+
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+
+class FakeUserRepository : UserRepository {
+
+ private val _users = MutableStateFlow<List<UserModel>>(emptyList())
+ override val users: Flow<List<UserModel>> = _users.asStateFlow()
+ override val selectedUser: Flow<UserModel> =
+ users.map { models -> models.first { model -> model.isSelected } }
+
+ private val _actions = MutableStateFlow<List<UserActionModel>>(emptyList())
+ override val actions: Flow<List<UserActionModel>> = _actions.asStateFlow()
+
+ private val _isActionableWhenLocked = MutableStateFlow(false)
+ override val isActionableWhenLocked: Flow<Boolean> = _isActionableWhenLocked.asStateFlow()
+
+ private var _isGuestUserAutoCreated: Boolean = false
+ override val isGuestUserAutoCreated: Boolean
+ get() = _isGuestUserAutoCreated
+ private var _isGuestUserResetting: Boolean = false
+ override val isGuestUserResetting: Boolean
+ get() = _isGuestUserResetting
+
+ fun setUsers(models: List<UserModel>) {
+ _users.value = models
+ }
+
+ fun setSelectedUser(userId: Int) {
+ check(_users.value.find { it.id == userId } != null) {
+ "Cannot select a user with ID $userId - no user with that ID found!"
+ }
+
+ setUsers(
+ _users.value.map { model ->
+ when {
+ model.isSelected && model.id != userId -> model.copy(isSelected = false)
+ !model.isSelected && model.id == userId -> model.copy(isSelected = true)
+ else -> model
+ }
+ }
+ )
+ }
+
+ fun setActions(models: List<UserActionModel>) {
+ _actions.value = models
+ }
+
+ fun setActionableWhenLocked(value: Boolean) {
+ _isActionableWhenLocked.value = value
+ }
+
+ fun setGuestUserAutoCreated(value: Boolean) {
+ _isGuestUserAutoCreated = value
+ }
+
+ fun setGuestUserResetting(value: Boolean) {
+ _isGuestUserResetting = value
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
new file mode 100644
index 0000000..6b466e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -0,0 +1,217 @@
+/*
+ * 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.user.data.repository
+
+import android.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class UserRepositoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var manager: UserManager
+ @Mock private lateinit var controller: UserSwitcherController
+ @Captor
+ private lateinit var userSwitchCallbackCaptor:
+ ArgumentCaptor<UserSwitcherController.UserSwitchCallback>
+
+ private lateinit var underTest: UserRepositoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(controller.addUsersFromLockScreen).thenReturn(MutableStateFlow(false))
+ whenever(controller.isGuestUserAutoCreated).thenReturn(false)
+ whenever(controller.isGuestUserResetting).thenReturn(false)
+
+ underTest =
+ UserRepositoryImpl(
+ appContext = context,
+ manager = manager,
+ controller = controller,
+ )
+ }
+
+ @Test
+ fun `users - registers for updates`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.users.onEach {}.launchIn(this)
+
+ verify(controller).addUserSwitchCallback(any())
+
+ job.cancel()
+ }
+
+ @Test
+ fun `users - unregisters from updates`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.users.onEach {}.launchIn(this)
+ verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
+
+ job.cancel()
+
+ verify(controller).removeUserSwitchCallback(userSwitchCallbackCaptor.value)
+ }
+
+ @Test
+ fun `users - does not include actions`() =
+ runBlocking(IMMEDIATE) {
+ whenever(controller.users)
+ .thenReturn(
+ arrayListOf(
+ createUserRecord(0, isSelected = true),
+ createActionRecord(UserActionModel.ADD_USER),
+ createUserRecord(1),
+ createUserRecord(2),
+ createActionRecord(UserActionModel.ADD_SUPERVISED_USER),
+ createActionRecord(UserActionModel.ENTER_GUEST_MODE),
+ )
+ )
+ var models: List<UserModel>? = null
+ val job = underTest.users.onEach { models = it }.launchIn(this)
+
+ assertThat(models).hasSize(3)
+ assertThat(models?.get(0)?.id).isEqualTo(0)
+ assertThat(models?.get(0)?.isSelected).isTrue()
+ assertThat(models?.get(1)?.id).isEqualTo(1)
+ assertThat(models?.get(1)?.isSelected).isFalse()
+ assertThat(models?.get(2)?.id).isEqualTo(2)
+ assertThat(models?.get(2)?.isSelected).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun selectedUser() =
+ runBlocking(IMMEDIATE) {
+ whenever(controller.users)
+ .thenReturn(
+ arrayListOf(
+ createUserRecord(0, isSelected = true),
+ createUserRecord(1),
+ createUserRecord(2),
+ )
+ )
+ var id: Int? = null
+ val job = underTest.selectedUser.map { it.id }.onEach { id = it }.launchIn(this)
+
+ assertThat(id).isEqualTo(0)
+
+ whenever(controller.users)
+ .thenReturn(
+ arrayListOf(
+ createUserRecord(0),
+ createUserRecord(1),
+ createUserRecord(2, isSelected = true),
+ )
+ )
+ verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
+ userSwitchCallbackCaptor.value.onUserSwitched()
+ assertThat(id).isEqualTo(2)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `actions - unregisters from updates`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.actions.onEach {}.launchIn(this)
+ verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
+
+ job.cancel()
+
+ verify(controller).removeUserSwitchCallback(userSwitchCallbackCaptor.value)
+ }
+
+ @Test
+ fun `actions - registers for updates`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.actions.onEach {}.launchIn(this)
+
+ verify(controller).addUserSwitchCallback(any())
+
+ job.cancel()
+ }
+
+ @Test
+ fun `actopms - does not include users`() =
+ runBlocking(IMMEDIATE) {
+ whenever(controller.users)
+ .thenReturn(
+ arrayListOf(
+ createUserRecord(0, isSelected = true),
+ createActionRecord(UserActionModel.ADD_USER),
+ createUserRecord(1),
+ createUserRecord(2),
+ createActionRecord(UserActionModel.ADD_SUPERVISED_USER),
+ createActionRecord(UserActionModel.ENTER_GUEST_MODE),
+ )
+ )
+ var models: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { models = it }.launchIn(this)
+
+ assertThat(models).hasSize(3)
+ assertThat(models?.get(0)).isEqualTo(UserActionModel.ADD_USER)
+ assertThat(models?.get(1)).isEqualTo(UserActionModel.ADD_SUPERVISED_USER)
+ assertThat(models?.get(2)).isEqualTo(UserActionModel.ENTER_GUEST_MODE)
+ job.cancel()
+ }
+
+ private fun createUserRecord(id: Int, isSelected: Boolean = false): UserRecord {
+ return UserRecord(
+ info = UserInfo(id, "name$id", 0),
+ isCurrent = isSelected,
+ )
+ }
+
+ private fun createActionRecord(action: UserActionModel): UserRecord {
+ return UserRecord(
+ isAddUser = action == UserActionModel.ADD_USER,
+ isAddSupervisedUser = action == UserActionModel.ADD_SUPERVISED_USER,
+ isGuest = action == UserActionModel.ENTER_GUEST_MODE,
+ )
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.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
new file mode 100644
index 0000000..e914e2e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -0,0 +1,213 @@
+/*
+ * 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.user.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class UserInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var controller: UserSwitcherController
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ private lateinit var underTest: UserInteractor
+
+ private lateinit var userRepository: FakeUserRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ userRepository = FakeUserRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ underTest =
+ UserInteractor(
+ repository = userRepository,
+ controller = controller,
+ activityStarter = activityStarter,
+ keyguardInteractor =
+ KeyguardInteractor(
+ repository = keyguardRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun `actions - not actionable when locked and locked - no actions`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(UserActionModel.values().toList())
+ userRepository.setActionableWhenLocked(false)
+ keyguardRepository.setKeyguardShowing(true)
+
+ var actions: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { actions = it }.launchIn(this)
+
+ assertThat(actions).isEmpty()
+ job.cancel()
+ }
+
+ @Test
+ fun `actions - not actionable when locked and not locked`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ )
+ )
+ userRepository.setActionableWhenLocked(false)
+ keyguardRepository.setKeyguardShowing(false)
+
+ var actions: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { actions = it }.launchIn(this)
+
+ assertThat(actions)
+ .isEqualTo(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `actions - actionable when locked and not locked`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ )
+ )
+ userRepository.setActionableWhenLocked(true)
+ keyguardRepository.setKeyguardShowing(false)
+
+ var actions: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { actions = it }.launchIn(this)
+
+ assertThat(actions)
+ .isEqualTo(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `actions - actionable when locked and locked`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ )
+ )
+ userRepository.setActionableWhenLocked(true)
+ keyguardRepository.setKeyguardShowing(true)
+
+ var actions: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { actions = it }.launchIn(this)
+
+ assertThat(actions)
+ .isEqualTo(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun selectUser() {
+ val userId = 3
+
+ underTest.selectUser(userId)
+
+ verify(controller).onUserSelected(eq(userId), nullable())
+ }
+
+ @Test
+ fun `executeAction - guest`() {
+ underTest.executeAction(UserActionModel.ENTER_GUEST_MODE)
+
+ verify(controller).createAndSwitchToGuestUser(nullable())
+ }
+
+ @Test
+ fun `executeAction - add user`() {
+ underTest.executeAction(UserActionModel.ADD_USER)
+
+ verify(controller).showAddUserDialog(nullable())
+ }
+
+ @Test
+ fun `executeAction - add supervised user`() {
+ underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
+
+ verify(controller).startSupervisedUserActivity()
+ }
+
+ @Test
+ fun `executeAction - manage users`() {
+ underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
+
+ verify(activityStarter).startActivity(any(), anyBoolean())
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
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
new file mode 100644
index 0000000..ef4500d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -0,0 +1,297 @@
+/*
+ * 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.user.ui.viewmodel
+
+import android.graphics.drawable.Drawable
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class UserSwitcherViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var controller: UserSwitcherController
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ private lateinit var underTest: UserSwitcherViewModel
+
+ private lateinit var userRepository: FakeUserRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var powerRepository: FakePowerRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ userRepository = FakeUserRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ powerRepository = FakePowerRepository()
+ underTest =
+ UserSwitcherViewModel.Factory(
+ userInteractor =
+ UserInteractor(
+ repository = userRepository,
+ controller = controller,
+ activityStarter = activityStarter,
+ keyguardInteractor =
+ KeyguardInteractor(
+ repository = keyguardRepository,
+ )
+ ),
+ powerInteractor =
+ PowerInteractor(
+ repository = powerRepository,
+ ),
+ )
+ .create(UserSwitcherViewModel::class.java)
+ }
+
+ @Test
+ fun users() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setUsers(
+ listOf(
+ UserModel(
+ id = 0,
+ name = Text.Loaded("zero"),
+ image = USER_IMAGE,
+ isSelected = true,
+ isSelectable = true,
+ ),
+ UserModel(
+ id = 1,
+ name = Text.Loaded("one"),
+ image = USER_IMAGE,
+ isSelected = false,
+ isSelectable = true,
+ ),
+ UserModel(
+ id = 2,
+ name = Text.Loaded("two"),
+ image = USER_IMAGE,
+ isSelected = false,
+ isSelectable = false,
+ ),
+ )
+ )
+
+ var userViewModels: List<UserViewModel>? = null
+ val job = underTest.users.onEach { userViewModels = it }.launchIn(this)
+
+ assertThat(userViewModels).hasSize(3)
+ assertUserViewModel(
+ viewModel = userViewModels?.get(0),
+ viewKey = 0,
+ name = "zero",
+ isSelectionMarkerVisible = true,
+ alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA,
+ isClickable = true,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels?.get(1),
+ viewKey = 1,
+ name = "one",
+ isSelectionMarkerVisible = false,
+ alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA,
+ isClickable = true,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels?.get(2),
+ viewKey = 2,
+ name = "two",
+ isSelectionMarkerVisible = false,
+ alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA,
+ isClickable = false,
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `maximumUserColumns - few users`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 2)
+ var value: Int? = null
+ val job = underTest.maximumUserColumns.onEach { value = it }.launchIn(this)
+
+ assertThat(value).isEqualTo(4)
+ job.cancel()
+ }
+
+ @Test
+ fun `maximumUserColumns - many users`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 5)
+ var value: Int? = null
+ val job = underTest.maximumUserColumns.onEach { value = it }.launchIn(this)
+
+ assertThat(value).isEqualTo(3)
+ job.cancel()
+ }
+
+ @Test
+ fun `isOpenMenuButtonVisible - has actions - true`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(UserActionModel.values().toList())
+
+ var isVisible: Boolean? = null
+ val job = underTest.isOpenMenuButtonVisible.onEach { isVisible = it }.launchIn(this)
+
+ assertThat(isVisible).isTrue()
+ job.cancel()
+ }
+
+ @Test
+ fun `isOpenMenuButtonVisible - no actions - false`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(emptyList())
+
+ var isVisible: Boolean? = null
+ val job = underTest.isOpenMenuButtonVisible.onEach { isVisible = it }.launchIn(this)
+
+ assertThat(isVisible).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun menu() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(UserActionModel.values().toList())
+ var isMenuVisible: Boolean? = null
+ val job = underTest.isMenuVisible.onEach { isMenuVisible = it }.launchIn(this)
+ assertThat(isMenuVisible).isFalse()
+
+ underTest.onOpenMenuButtonClicked()
+ assertThat(isMenuVisible).isTrue()
+
+ underTest.onMenuClosed()
+ assertThat(isMenuVisible).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isFinishRequested - finishes when user is switched`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 2)
+ var isFinishRequested: Boolean? = null
+ val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
+ assertThat(isFinishRequested).isFalse()
+
+ userRepository.setSelectedUser(1)
+ yield()
+ assertThat(isFinishRequested).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isFinishRequested - finishes when the screen turns off`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 2)
+ powerRepository.setInteractive(true)
+ var isFinishRequested: Boolean? = null
+ val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
+ assertThat(isFinishRequested).isFalse()
+
+ powerRepository.setInteractive(false)
+ yield()
+ assertThat(isFinishRequested).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isFinishRequested - finishes when cancel button is clicked`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 2)
+ powerRepository.setInteractive(true)
+ var isFinishRequested: Boolean? = null
+ val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
+ assertThat(isFinishRequested).isFalse()
+
+ underTest.onCancelButtonClicked()
+ yield()
+ assertThat(isFinishRequested).isTrue()
+
+ underTest.onFinished()
+ yield()
+ assertThat(isFinishRequested).isFalse()
+
+ job.cancel()
+ }
+
+ private fun setUsers(count: Int) {
+ userRepository.setUsers(
+ (0 until count).map { index ->
+ UserModel(
+ id = index,
+ name = Text.Loaded("$index"),
+ image = USER_IMAGE,
+ isSelected = index == 0,
+ isSelectable = true,
+ )
+ }
+ )
+ }
+
+ private fun assertUserViewModel(
+ viewModel: UserViewModel?,
+ viewKey: Int,
+ name: String,
+ isSelectionMarkerVisible: Boolean,
+ alpha: Float,
+ isClickable: Boolean,
+ ) {
+ checkNotNull(viewModel)
+ assertThat(viewModel.viewKey).isEqualTo(viewKey)
+ assertThat(viewModel.name).isEqualTo(Text.Loaded(name))
+ assertThat(viewModel.isSelectionMarkerVisible).isEqualTo(isSelectionMarkerVisible)
+ assertThat(viewModel.alpha).isEqualTo(alpha)
+ assertThat(viewModel.onClicked != null).isEqualTo(isClickable)
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ private val USER_IMAGE = mock<Drawable>()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 092e82c..460b71f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -127,6 +127,17 @@
)
)
}
+
+ @Test
+ fun dontEmitFirstEvent() = runBlocking {
+ assertThatFlow(flowOf(setOf(1, 2), setOf(2, 3)).setChanges(emitFirstEvent = false))
+ .emitsExactly(
+ SetChanges(
+ removed = setOf(1),
+ added = setOf(3),
+ )
+ )
+ }
}
private fun <T> assertThatFlow(flow: Flow<T>) = object {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index f539dbd..d058053 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -118,3 +118,17 @@
*/
inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
kotlinArgumentCaptor<T>().apply { block() }.value
+
+/**
+ * Variant of [withArgCaptor] for capturing multiple arguments.
+ *
+ * val captor = argumentCaptor<Foo>()
+ * verify(...).someMethod(captor.capture())
+ * val captured: List<Foo> = captor.allValues
+ *
+ * becomes:
+ *
+ * val capturedList = captureMany<Foo> { verify(...).someMethod(capture()) }
+ */
+inline fun <reified T : Any> captureMany(block: KotlinArgumentCaptor<T>.() -> Unit): List<T> =
+ kotlinArgumentCaptor<T>().apply{ block() }.allValues
diff --git a/services/Android.bp b/services/Android.bp
index 4f80a98..decfa77 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -57,6 +57,7 @@
// retracing infra.
optimize: false,
shrink: true,
+ ignore_warnings: false,
proguard_flags_files: ["proguard.flags"],
},
// Note: Optimizations are disabled by default if unspecified in
@@ -175,6 +176,8 @@
"android.hidl.manager-V1.0-java",
"framework-tethering.stubs.module_lib",
"service-art.stubs.system_server",
+ "service-permission.stubs.system_server",
+ "service-sdksandbox.stubs.system_server",
],
// Uncomment to enable output of certain warnings (deprecated, unchecked)
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 990e963..f55cfb1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -957,10 +957,11 @@
// current state of the windows as the window manager may be delaying
// the computation for performance reasons.
boolean shouldComputeWindows = false;
- int displayId = Display.INVALID_DISPLAY;
+ int displayId = event.getDisplayId();
synchronized (mLock) {
final int windowId = event.getWindowId();
- if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
+ && displayId == Display.INVALID_DISPLAY) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
resolvedUserId, windowId);
event.setDisplayId(displayId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index d33e7b2..7c68c8a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -19,7 +19,6 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -275,22 +274,23 @@
* Sets the active flag of the window according to given windowId, others set to inactive.
*
* @param windowId The windowId
+ * @return {@code true} if the window is in this display, {@code false} otherwise.
*/
- void setActiveWindowLocked(int windowId) {
+ boolean setActiveWindowLocked(int windowId) {
+ boolean foundWindow = false;
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
if (window.getId() == windowId) {
window.setActive(true);
- mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
- AccessibilityEvent.obtainWindowsChangedEvent(windowId,
- AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
+ foundWindow = true;
} else {
window.setActive(false);
}
}
}
+ return foundWindow;
}
/**
@@ -298,24 +298,23 @@
* unfocused.
*
* @param windowId The windowId
+ * @return {@code true} if the window is in this display, {@code false} otherwise.
*/
- void setAccessibilityFocusedWindowLocked(int windowId) {
+ boolean setAccessibilityFocusedWindowLocked(int windowId) {
+ boolean foundWindow = false;
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
if (window.getId() == windowId) {
- mAccessibilityFocusedDisplayId = mDisplayId;
window.setAccessibilityFocused(true);
- mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
- AccessibilityEvent.obtainWindowsChangedEvent(
- windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
-
+ foundWindow = true;
} else {
window.setAccessibilityFocused(false);
}
}
}
+ return foundWindow;
}
/**
@@ -704,7 +703,7 @@
final AccessibilityWindowInfo window = oldWindows.get(i);
if (mA11yWindowInfoById.get(window.getId()) == null) {
events.add(AccessibilityEvent.obtainWindowsChangedEvent(
- window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED));
+ mDisplayId, window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED));
}
}
@@ -714,13 +713,13 @@
final AccessibilityWindowInfo newWindow = mWindows.get(i);
final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId());
if (oldWindow == null) {
- events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(mDisplayId,
newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED));
} else {
int changes = newWindow.differenceFrom(oldWindow);
if (changes != 0) {
events.add(AccessibilityEvent.obtainWindowsChangedEvent(
- newWindow.getId(), changes));
+ mDisplayId, newWindow.getId(), changes));
}
}
}
@@ -1522,38 +1521,59 @@
private void setActiveWindowLocked(int windowId) {
if (mActiveWindowId != windowId) {
- mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
- AccessibilityEvent.obtainWindowsChangedEvent(
+ List<AccessibilityEvent> events = new ArrayList<>(2);
+ if (mActiveWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ final DisplayWindowsObserver observer =
+ getDisplayWindowObserverByWindowIdLocked(mActiveWindowId);
+ if (observer != null) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId,
mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
+ }
+ }
mActiveWindowId = windowId;
// Goes through all windows for each display.
final int count = mDisplayWindowsObservers.size();
for (int i = 0; i < count; i++) {
final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
- if (observer != null) {
- observer.setActiveWindowLocked(windowId);
+ if (observer != null && observer.setActiveWindowLocked(windowId)) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId,
+ windowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
}
}
+
+ for (final AccessibilityEvent event : events) {
+ mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(event);
+ }
}
}
private void setAccessibilityFocusedWindowLocked(int windowId) {
if (mAccessibilityFocusedWindowId != windowId) {
- mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
- AccessibilityEvent.obtainWindowsChangedEvent(
- mAccessibilityFocusedWindowId,
- WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+ List<AccessibilityEvent> events = new ArrayList<>(2);
+ if (mAccessibilityFocusedDisplayId != Display.INVALID_DISPLAY
+ && mAccessibilityFocusedWindowId
+ != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+ mAccessibilityFocusedDisplayId, mAccessibilityFocusedWindowId,
+ AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+ }
mAccessibilityFocusedWindowId = windowId;
// Goes through all windows for each display.
final int count = mDisplayWindowsObservers.size();
for (int i = 0; i < count; i++) {
final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
- if (observer != null) {
- observer.setAccessibilityFocusedWindowLocked(windowId);
+ if (observer != null && observer.setAccessibilityFocusedWindowLocked(windowId)) {
+ mAccessibilityFocusedDisplayId = observer.mDisplayId;
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId,
+ windowId, AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
}
}
+
+ for (final AccessibilityEvent event : events) {
+ mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(event);
+ }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 01cee33..b5fdaca 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -41,7 +41,12 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
import android.service.autofill.Dataset;
+import android.text.TextUtils;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
@@ -61,7 +66,7 @@
* Reasons why presentation was not shown. These are wrappers around
* {@link com.android.os.AtomsProto.AutofillPresentationEventReported.PresentationEventResult}.
*/
- @IntDef(prefix = { "NOT_SHOWN_REASON" }, value = {
+ @IntDef(prefix = {"NOT_SHOWN_REASON"}, value = {
NOT_SHOWN_REASON_ANY_SHOWN,
NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED,
NOT_SHOWN_REASON_VIEW_CHANGED,
@@ -72,6 +77,7 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface NotShownReason {}
+
public static final int NOT_SHOWN_REASON_ANY_SHOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
public static final int NOT_SHOWN_REASON_VIEW_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
@@ -172,6 +178,45 @@
});
}
+ public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
+ mEventInternal.ifPresent(event -> {
+ event.mDisplayPresentationType = UI_TYPE_INLINE;
+ String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD, userId);
+ if (TextUtils.isEmpty(imeString)) {
+ Slog.w(TAG, "No default IME found");
+ return;
+ }
+ ComponentName imeComponent = ComponentName.unflattenFromString(imeString);
+ if (imeComponent == null) {
+ Slog.w(TAG, "No default IME found");
+ return;
+ }
+ int imeUid;
+ String packageName = imeComponent.getPackageName();
+ try {
+ imeUid = context.getPackageManager().getApplicationInfoAsUser(packageName,
+ PackageManager.ApplicationInfoFlags.of(0), userId).uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Couldn't find packageName: " + packageName);
+ return;
+ }
+ event.mInlineSuggestionHostUid = imeUid;
+ });
+ }
+
+ public void maybeSetAutofillServiceUid(int uid) {
+ mEventInternal.ifPresent(event -> {
+ event.mAutofillServiceUid = uid;
+ });
+ }
+
+ public void maybeSetIsNewRequest(boolean isRequestTriggered) {
+ mEventInternal.ifPresent(event -> {
+ event.mIsRequestTriggered = isRequestTriggered;
+ });
+ }
+
public void logAndEndEvent() {
if (!mEventInternal.isPresent()) {
Slog.w(TAG, "Shouldn't be logging AutofillPresentationEventReported again for same "
@@ -190,7 +235,10 @@
+ " mCountNotShownImePresentationNotDrawn="
+ event.mCountNotShownImePresentationNotDrawn
+ " mCountNotShownImeUserNotSeen=" + event.mCountNotShownImeUserNotSeen
- + " mDisplayPresentationType=" + event.mDisplayPresentationType);
+ + " mDisplayPresentationType=" + event.mDisplayPresentationType
+ + " mAutofillServiceUid=" + event.mAutofillServiceUid
+ + " mInlineSuggestionHostUid=" + event.mInlineSuggestionHostUid
+ + " mIsRequestTriggered=" + event.mIsRequestTriggered);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -208,7 +256,10 @@
event.mCountFilteredUserTyping,
event.mCountNotShownImePresentationNotDrawn,
event.mCountNotShownImeUserNotSeen,
- event.mDisplayPresentationType);
+ event.mDisplayPresentationType,
+ event.mAutofillServiceUid,
+ event.mInlineSuggestionHostUid,
+ event.mIsRequestTriggered);
mEventInternal = Optional.empty();
}
@@ -222,6 +273,9 @@
int mCountNotShownImePresentationNotDrawn;
int mCountNotShownImeUserNotSeen;
int mDisplayPresentationType = AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
+ int mAutofillServiceUid = -1;
+ int mInlineSuggestionHostUid = -1;
+ boolean mIsRequestTriggered;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1fea539..d5945a5 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -68,6 +68,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ServiceInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -79,6 +80,7 @@
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Parcelable;
+import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -3040,6 +3042,7 @@
mSessionFlags.mFillDialogDisabled = true;
}
mPresentationStatsEventLogger.startNewEvent();
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
break;
case ACTION_VALUE_CHANGED:
@@ -3129,6 +3132,7 @@
}
mPresentationStatsEventLogger.startNewEvent();
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
return;
}
@@ -3373,7 +3377,8 @@
// shown, inflated, and filtered.
mPresentationStatsEventLogger.maybeSetCountShown(
response.getDatasets(), mCurrentViewId);
- mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_INLINE);
+ mPresentationStatsEventLogger.maybeSetInlinePresentationAndSuggestionHostUid(
+ mContext, userId);
return;
}
}
@@ -4750,4 +4755,9 @@
return "UNKNOWN_SESSION_STATE_" + sessionState;
}
}
+
+ private int getAutofillServiceUid() {
+ ServiceInfo serviceInfo = mService.getServiceInfo();
+ return serviceInfo == null ? Process.INVALID_UID : serviceInfo.applicationInfo.uid;
+ }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index 23e4e14..3814591 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -31,9 +31,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.PerUser;
-import com.android.internal.util.CollectionUtils;
+import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -69,32 +70,22 @@
private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec
- interface Callback {
- /**
- * @return {@code true} if should schedule rebinding.
- * {@code false} if we do not need to rebind.
- */
- boolean onCompanionApplicationBindingDied(
- @UserIdInt int userId, @NonNull String packageName);
-
- /**
- * Callback after timeout for previously scheduled rebind has passed.
- */
- void onRebindCompanionApplicationTimeout(
- @UserIdInt int userId, @NonNull String packageName);
- }
-
private final @NonNull Context mContext;
- private final @NonNull Callback mCallback;
+ private final @NonNull AssociationStore mAssociationStore;
+ private final @NonNull CompanionDevicePresenceMonitor mDevicePresenceMonitor;
private final @NonNull CompanionServicesRegister mCompanionServicesRegister;
+
@GuardedBy("mBoundCompanionApplications")
private final @NonNull AndroidPackageMap<List<CompanionDeviceServiceConnector>>
mBoundCompanionApplications;
+ @GuardedBy("mScheduledForRebindingCompanionApplications")
private final @NonNull AndroidPackageMap<Boolean> mScheduledForRebindingCompanionApplications;
- CompanionApplicationController(Context context, Callback callback) {
+ CompanionApplicationController(Context context, AssociationStore associationStore,
+ CompanionDevicePresenceMonitor companionDevicePresenceMonitor) {
mContext = context;
- mCallback = callback;
+ mAssociationStore = associationStore;
+ mDevicePresenceMonitor = companionDevicePresenceMonitor;
mCompanionServicesRegister = new CompanionServicesRegister();
mBoundCompanionApplications = new AndroidPackageMap<>();
mScheduledForRebindingCompanionApplications = new AndroidPackageMap<>();
@@ -125,21 +116,26 @@
return;
}
- final List<CompanionDeviceServiceConnector> serviceConnectors;
+ final List<CompanionDeviceServiceConnector> serviceConnectors = new ArrayList<>();
synchronized (mBoundCompanionApplications) {
if (mBoundCompanionApplications.containsValueForPackage(userId, packageName)) {
if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is ALREADY bound.");
return;
}
- serviceConnectors = CollectionUtils.map(companionServices, componentName ->
- CompanionDeviceServiceConnector.newInstance(mContext, userId,
- componentName, isSelfManaged));
+ for (int i = 0; i < companionServices.size(); i++) {
+ boolean isPrimary = i == 0;
+ serviceConnectors.add(CompanionDeviceServiceConnector.newInstance(mContext, userId,
+ companionServices.get(i), isSelfManaged, isPrimary));
+ }
+
mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
}
- // The first connector in the list is always the primary connector: set a listener to it.
- serviceConnectors.get(0).setListener(this::onPrimaryServiceBindingDied);
+ // Set listeners for both Primary and Secondary connectors.
+ for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) {
+ serviceConnector.setListener(this::onBinderDied);
+ }
// Now "bind" all the connectors: the primary one and the rest of them.
for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) {
@@ -154,9 +150,15 @@
if (DEBUG) Log.i(TAG, "unbind() u" + userId + "/" + packageName);
final List<CompanionDeviceServiceConnector> serviceConnectors;
+
synchronized (mBoundCompanionApplications) {
serviceConnectors = mBoundCompanionApplications.removePackage(userId, packageName);
}
+
+ synchronized (mScheduledForRebindingCompanionApplications) {
+ mScheduledForRebindingCompanionApplications.removePackage(userId, packageName);
+ }
+
if (serviceConnectors == null) {
if (DEBUG) {
Log.e(TAG, "unbindCompanionApplication(): "
@@ -180,24 +182,59 @@
}
}
- private void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName) {
- mScheduledForRebindingCompanionApplications.setValueForPackage(userId, packageName, true);
+ private void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName,
+ CompanionDeviceServiceConnector serviceConnector) {
+ Slog.i(TAG, "scheduleRebinding() " + userId + "/" + packageName);
+ if (isRebindingCompanionApplicationScheduled(userId, packageName)) {
+ if (DEBUG) {
+ Log.i(TAG, "CompanionApplication rebinding has been scheduled, skipping "
+ + serviceConnector.getComponentName());
+ }
+ return;
+ }
+
+ if (serviceConnector.isPrimary()) {
+ synchronized (mScheduledForRebindingCompanionApplications) {
+ mScheduledForRebindingCompanionApplications.setValueForPackage(
+ userId, packageName, true);
+ }
+ }
+
+ // Rebinding in 10 seconds.
Handler.getMain().postDelayed(() ->
- onRebindingCompanionApplicationTimeout(userId, packageName), REBIND_TIMEOUT);
+ onRebindingCompanionApplicationTimeout(userId, packageName, serviceConnector),
+ REBIND_TIMEOUT);
}
- boolean isRebindingCompanionApplicationScheduled(
+ private boolean isRebindingCompanionApplicationScheduled(
@UserIdInt int userId, @NonNull String packageName) {
- return mScheduledForRebindingCompanionApplications
- .containsValueForPackage(userId, packageName);
+ synchronized (mScheduledForRebindingCompanionApplications) {
+ return mScheduledForRebindingCompanionApplications.containsValueForPackage(
+ userId, packageName);
+ }
}
private void onRebindingCompanionApplicationTimeout(
- @UserIdInt int userId, @NonNull String packageName) {
- mScheduledForRebindingCompanionApplications.removePackage(userId, packageName);
+ @UserIdInt int userId, @NonNull String packageName,
+ @NonNull CompanionDeviceServiceConnector serviceConnector) {
+ // Re-mark the application is bound.
+ if (serviceConnector.isPrimary()) {
+ synchronized (mBoundCompanionApplications) {
+ if (!mBoundCompanionApplications.containsValueForPackage(userId, packageName)) {
+ List<CompanionDeviceServiceConnector> serviceConnectors =
+ Collections.singletonList(serviceConnector);
+ mBoundCompanionApplications.setValueForPackage(userId, packageName,
+ serviceConnectors);
+ }
+ }
- mCallback.onRebindCompanionApplicationTimeout(userId, packageName);
+ synchronized (mScheduledForRebindingCompanionApplications) {
+ mScheduledForRebindingCompanionApplications.removePackage(userId, packageName);
+ }
+ }
+
+ serviceConnector.connect();
}
void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
@@ -272,19 +309,27 @@
}
}
- private void onPrimaryServiceBindingDied(@UserIdInt int userId, @NonNull String packageName) {
- if (DEBUG) Log.i(TAG, "onPrimaryServiceBindingDied() u" + userId + "/" + packageName);
+ /**
+ * Rebinding for Self-Managed secondary services OR Non-Self-Managed services.
+ */
+ private void onBinderDied(@UserIdInt int userId, @NonNull String packageName,
+ @NonNull CompanionDeviceServiceConnector serviceConnector) {
- // First: mark as NOT bound.
+ boolean isPrimary = serviceConnector.isPrimary();
+ Slog.i(TAG, "onBinderDied() u" + userId + "/" + packageName + " isPrimary: " + isPrimary);
+
+ // First: Only mark not BOUND for primary service.
synchronized (mBoundCompanionApplications) {
- mBoundCompanionApplications.removePackage(userId, packageName);
+ if (serviceConnector.isPrimary()) {
+ mBoundCompanionApplications.removePackage(userId, packageName);
+ }
}
- // Second: invoke callback, schedule rebinding if needed.
- final boolean shouldScheduleRebind =
- mCallback.onCompanionApplicationBindingDied(userId, packageName);
+ // Second: schedule rebinding if needed.
+ final boolean shouldScheduleRebind = shouldScheduleRebind(userId, packageName, isPrimary);
+
if (shouldScheduleRebind) {
- scheduleRebinding(userId, packageName);
+ scheduleRebinding(userId, packageName, serviceConnector);
}
}
@@ -297,6 +342,37 @@
return connectors != null ? connectors.get(0) : null;
}
+ private boolean shouldScheduleRebind(int userId, String packageName, boolean isPrimary) {
+ // Make sure do not schedule rebind for the case ServiceConnector still gets callback after
+ // app is uninstalled.
+ boolean stillAssociated = false;
+
+ for (AssociationInfo ai :
+ mAssociationStore.getAssociationsForPackage(userId, packageName)) {
+ final int associationId = ai.getId();
+ stillAssociated = true;
+
+ if (ai.isSelfManaged()) {
+ // Do not rebind if primary one is died for selfManaged application.
+ if (isPrimary
+ && mDevicePresenceMonitor.isDevicePresent(associationId)) {
+ mDevicePresenceMonitor.onSelfManagedDeviceReporterBinderDied(associationId);
+ return false;
+ }
+ // Do not rebind if both primary and secondary services are died for
+ // selfManaged application.
+ if (!isCompanionApplicationBound(userId, packageName)) {
+ return false;
+ }
+ } else if (ai.isNotifyOnDeviceNearby()) {
+ // Always rebind for non-selfManaged devices.
+ return true;
+ }
+ }
+
+ return stillAssociated;
+ }
+
private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> {
@Override
public synchronized @NonNull Map<String, List<ComponentName>> forUser(
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index d39d6ae..85d3140 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -233,7 +233,7 @@
mAssociationRequestsProcessor = new AssociationRequestsProcessor(
/* cdmService */this, mAssociationStore);
mCompanionAppController = new CompanionApplicationController(
- context, mApplicationControllerCallback);
+ context, mAssociationStore, mDevicePresenceMonitor);
mTransportManager = new CompanionTransportManager(context);
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
mSystemDataTransferRequestStore, mTransportManager);
@@ -387,25 +387,6 @@
mCompanionAppController.unbindCompanionApplication(userId, packageName);
}
- private boolean onCompanionApplicationBindingDiedInternal(
- @UserIdInt int userId, @NonNull String packageName) {
- for (AssociationInfo ai :
- mAssociationStore.getAssociationsForPackage(userId, packageName)) {
- final int associationId = ai.getId();
- if (ai.isSelfManaged()
- && mDevicePresenceMonitor.isDevicePresent(associationId)) {
- mDevicePresenceMonitor.onSelfManagedDeviceReporterBinderDied(associationId);
- }
- }
- // TODO(b/218613015): implement.
- return false;
- }
-
- private void onRebindCompanionApplicationTimeoutInternal(
- @UserIdInt int userId, @NonNull String packageName) {
- // TODO(b/218613015): implement.
- }
-
/**
* @return whether the package should be bound (i.e. at least one of the devices associated with
* the package is currently present).
@@ -499,6 +480,10 @@
for (AssociationInfo association : associationsForPackage) {
mAssociationStore.removeAssociation(association.getId());
}
+ // Clear role holders
+ for (AssociationInfo association : associationsForPackage) {
+ maybeRemoveRoleHolderForAssociation(association);
+ }
mCompanionAppController.onPackagesChanged(userId);
}
@@ -1287,19 +1272,6 @@
}
};
- private final CompanionApplicationController.Callback mApplicationControllerCallback =
- new CompanionApplicationController.Callback() {
- @Override
- public boolean onCompanionApplicationBindingDied(int userId, @NonNull String packageName) {
- return onCompanionApplicationBindingDiedInternal(userId, packageName);
- }
-
- @Override
- public void onRebindCompanionApplicationTimeout(int userId, @NonNull String packageName) {
- onRebindCompanionApplicationTimeoutInternal(userId, packageName);
- }
- };
-
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
public void onPackageRemoved(String packageName, int uid) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
index 35a7289..93cc611 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -48,7 +48,8 @@
/** Listener for changes to the state of the {@link CompanionDeviceServiceConnector} */
interface Listener {
- void onBindingDied(@UserIdInt int userId, @NonNull String packageName);
+ void onBindingDied(@UserIdInt int userId, @NonNull String packageName,
+ @NonNull CompanionDeviceServiceConnector serviceConnector);
}
private final @UserIdInt int mUserId;
@@ -57,6 +58,7 @@
// installs a listener to the primary ServiceConnector), hence we should always null-check the
// reference before calling on it.
private @Nullable Listener mListener;
+ private boolean mIsPrimary;
/**
* Create a CompanionDeviceServiceConnector instance.
@@ -74,17 +76,20 @@
* service importance level should be higher than 125.
*/
static CompanionDeviceServiceConnector newInstance(@NonNull Context context,
- @UserIdInt int userId, @NonNull ComponentName componentName, boolean isSelfManaged) {
+ @UserIdInt int userId, @NonNull ComponentName componentName, boolean isSelfManaged,
+ boolean isPrimary) {
final int bindingFlags = isSelfManaged ? BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE
: BIND_ALMOST_PERCEPTIBLE;
- return new CompanionDeviceServiceConnector(context, userId, componentName, bindingFlags);
+ return new CompanionDeviceServiceConnector(
+ context, userId, componentName, bindingFlags, isPrimary);
}
private CompanionDeviceServiceConnector(@NonNull Context context, @UserIdInt int userId,
- @NonNull ComponentName componentName, int bindingFlags) {
+ @NonNull ComponentName componentName, int bindingFlags, boolean isPrimary) {
super(context, buildIntent(componentName), bindingFlags, userId, null);
mUserId = userId;
mComponentName = componentName;
+ mIsPrimary = isPrimary;
}
void setListener(@Nullable Listener listener) {
@@ -110,6 +115,14 @@
post(it -> unbind());
}
+ boolean isPrimary() {
+ return mIsPrimary;
+ }
+
+ ComponentName getComponentName() {
+ return mComponentName;
+ }
+
@Override
protected void onServiceConnectionStatusChanged(
@NonNull ICompanionDeviceService service, boolean isConnected) {
@@ -119,16 +132,6 @@
}
}
- // This method is only called when app is force-closed via settings,
- // but does not handle crashes. Binder death should be handled in #binderDied()
- @Override
- public void onBindingDied(@NonNull ComponentName name) {
- // IMPORTANT: call super! this will also invoke binderDied()
- super.onBindingDied(name);
-
- if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString());
- }
-
@Override
public void binderDied() {
super.binderDied();
@@ -137,7 +140,7 @@
// Handle primary process being killed
if (mListener != null) {
- mListener.onBindingDied(mUserId, mComponentName.getPackageName());
+ mListener.onBindingDied(mUserId, mComponentName.getPackageName(), this);
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 593a63c..fc628cf 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -250,7 +250,9 @@
// The callback is fired only when windowFlags are changed. To let VirtualDevice owner
// aware that the virtual display has a secure window on top.
if ((windowFlags & FLAG_SECURE) != 0) {
- mSecureWindowCallback.onSecureWindowShown(mDisplayId, activityInfo.applicationInfo.uid);
+ // Post callback on the main thread, so it doesn't block activity launching.
+ mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(mDisplayId,
+ activityInfo.applicationInfo.uid));
}
if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) {
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index b33d84c..c713a41 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -314,6 +314,14 @@
private static final DropboxRateLimiter sDropboxRateLimiter = new DropboxRateLimiter();
/**
+ * Reset the dropbox rate limiter.
+ */
+ @VisibleForTesting
+ public static void resetDropboxRateLimiter() {
+ sDropboxRateLimiter.reset();
+ }
+
+ /**
* Add a tombstone to the DropBox.
*
* @param ctx Context
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 32dc470..3f1d1fe 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -28,6 +28,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.IVpnManager;
@@ -775,6 +776,12 @@
};
private void onUserStarted(int userId) {
+ UserInfo user = mUserManager.getUserInfo(userId);
+ if (user == null) {
+ logw("Started user doesn't exist. UserId: " + userId);
+ return;
+ }
+
synchronized (mVpns) {
Vpn userVpn = mVpns.get(userId);
if (userVpn != null) {
@@ -783,7 +790,8 @@
}
userVpn = mDeps.createVpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId);
mVpns.put(userId, userVpn);
- if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
+
+ if (user.isPrimary() && isLockdownVpnEnabled()) {
updateLockdownVpn();
}
}
@@ -898,9 +906,15 @@
}
private void onUserUnlocked(int userId) {
+ UserInfo user = mUserManager.getUserInfo(userId);
+ if (user == null) {
+ logw("Unlocked user doesn't exist. UserId: " + userId);
+ return;
+ }
+
synchronized (mVpns) {
// User present may be sent because of an unlock, which might mean an unlocked keystore.
- if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
+ if (user.isPrimary() && isLockdownVpnEnabled()) {
updateLockdownVpn();
} else {
startAlwaysOnVpn(userId);
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 2eaf323..04bd869 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -667,6 +667,7 @@
+ " Not enabling adbwifi.");
Settings.Global.putInt(mContentResolver,
Settings.Global.ADB_WIFI_ENABLED, 0);
+ return;
}
// Check for network change
@@ -675,6 +676,7 @@
Slog.e(TAG, "Unable to get the wifi ap's BSSID. Disabling adbwifi.");
Settings.Global.putInt(mContentResolver,
Settings.Global.ADB_WIFI_ENABLED, 0);
+ return;
}
synchronized (mAdbConnectionInfo) {
if (!bssid.equals(mAdbConnectionInfo.getBSSID())) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a580f98..3b299a2 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -192,6 +192,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
public final class ActiveServices {
@@ -222,6 +223,11 @@
| ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
| ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION;
+ // Keep track of number of foreground services and number of apps that have foreground
+ // services in the device. This field is made to be directly accessed without holding AMS lock.
+ static final AtomicReference<Pair<Integer, Integer>> sNumForegroundServices =
+ new AtomicReference(new Pair<>(0, 0));
+
// Foreground service is stopped for unknown reason.
static final int FGS_STOP_REASON_UNKNOWN = 0;
// Foreground service is stopped by app calling Service.stopForeground().
@@ -456,6 +462,7 @@
final ArrayList<ServiceRecord> mStartingBackground = new ArrayList<>();
final ArrayMap<String, ActiveForegroundApp> mActiveForegroundApps = new ArrayMap<>();
+
boolean mActiveForegroundAppsChanged;
static final int MSG_BG_START_TIMEOUT = 1;
@@ -2027,6 +2034,7 @@
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
0, FGS_STOP_REASON_UNKNOWN);
+ updateNumForegroundServicesLocked();
}
// Even if the service is already a FGS, we need to update the notification,
// so we need to call it again.
@@ -2118,6 +2126,7 @@
mAm.updateLruProcessLocked(r.app, false, null);
updateServiceForegroundLocked(r.app.mServices, true);
}
+ updateNumForegroundServicesLocked();
}
}
}
@@ -4836,6 +4845,7 @@
}
smap.ensureNotStartingBackgroundLocked(r);
+ updateNumForegroundServicesLocked();
}
private void dropFgsNotificationStateLocked(ServiceRecord r) {
@@ -7040,6 +7050,10 @@
fgsStopReasonToString(fgsStopReason));
}
+ private void updateNumForegroundServicesLocked() {
+ sNumForegroundServices.set(mAm.mProcessList.getNumForegroundServices());
+ }
+
boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
String callingPackage) {
return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2cf24fa..26899f1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -149,6 +149,7 @@
import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD;
import static com.android.server.wm.ActivityTaskManagerService.DUMP_STARTER_CMD;
import static com.android.server.wm.ActivityTaskManagerService.DUMP_TOP_RESUMED_ACTIVITY;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_VISIBLE_ACTIVITIES;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString;
@@ -376,6 +377,7 @@
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.AlarmManagerInternal;
+import com.android.server.BootReceiver;
import com.android.server.DeviceIdleInternal;
import com.android.server.DisplayThread;
import com.android.server.IntentResolver;
@@ -2412,13 +2414,13 @@
mBroadcastQueues = new BroadcastQueue[4];
mFgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
- "foreground", foreConstants, false);
+ "foreground", foreConstants, false, ProcessList.SCHED_GROUP_DEFAULT);
mBgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
- "background", backConstants, true);
+ "background", backConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
mBgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
- "offload_bg", offloadConstants, true);
+ "offload_bg", offloadConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
mFgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
- "offload_fg", foreConstants, true);
+ "offload_fg", foreConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
mBroadcastQueues[2] = mBgOffloadBroadcastQueue;
@@ -3263,13 +3265,20 @@
if (thread == null) {
return null;
}
+ return getRecordForAppLOSP(thread.asBinder());
+ }
- ProcessRecord record = mProcessList.getLRURecordForAppLOSP(thread);
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ ProcessRecord getRecordForAppLOSP(IBinder threadBinder) {
+ if (threadBinder == null) {
+ return null;
+ }
+
+ ProcessRecord record = mProcessList.getLRURecordForAppLOSP(threadBinder);
if (record != null) return record;
// Validation: if it isn't in the LRU list, it shouldn't exist, but let's
// double-check that.
- final IBinder threadBinder = thread.asBinder();
final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
mProcessList.getProcessNamesLOSP().getMap();
for (int i = pmap.size()-1; i >= 0; i--) {
@@ -9555,7 +9564,8 @@
|| DUMP_LASTANR_CMD.equals(cmd) || DUMP_LASTANR_TRACES_CMD.equals(cmd)
|| DUMP_STARTER_CMD.equals(cmd) || DUMP_CONTAINERS_CMD.equals(cmd)
|| DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)
- || DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)) {
+ || DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)
+ || DUMP_VISIBLE_ACTIVITIES.equals(cmd)) {
mAtmInternal.dump(
cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
} else if ("binder-proxies".equals(cmd)) {
@@ -10649,7 +10659,7 @@
if (!onlyHistory && !onlyReceivers && dumpAll) {
pw.println();
for (BroadcastQueue queue : mBroadcastQueues) {
- pw.println(" Queue " + queue.toString() + ": " + queue.describeState());
+ pw.println(" Queue " + queue.toString() + ": " + queue.describeStateLocked());
}
pw.println(" mHandler:");
mHandler.dump(new PrintWriterPrinter(pw), " ");
@@ -13370,7 +13380,7 @@
final BroadcastRecord r = rl.curBroadcast;
if (r != null) {
final boolean doNext = r.queue.finishReceiverLocked(
- receiver.asBinder(), r.resultCode, r.resultData, r.resultExtras,
+ rl.app, r.resultCode, r.resultData, r.resultExtras,
r.resultAbort, false);
if (doNext) {
doTrim = true;
@@ -14538,9 +14548,9 @@
}
}
- public void finishReceiver(IBinder who, int resultCode, String resultData,
+ public void finishReceiver(IBinder caller, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + who);
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + caller);
// Refuse possible leaked file descriptors
if (resultExtras != null && resultExtras.hasFileDescriptors()) {
@@ -14549,12 +14559,15 @@
final long origId = Binder.clearCallingIdentity();
try {
- boolean doNext = false;
- BroadcastRecord r;
- BroadcastQueue queue;
synchronized(this) {
- queue = broadcastQueueForFlags(flags);
- doNext = queue.finishReceiverLocked(who, resultCode,
+ final ProcessRecord callerApp = getRecordForAppLOSP(caller);
+ if (callerApp == null) {
+ Slog.w(TAG, "finishReceiver: no app for " + caller);
+ return;
+ }
+
+ final BroadcastQueue queue = broadcastQueueForFlags(flags);
+ queue.finishReceiverLocked(callerApp, resultCode,
resultData, resultExtras, resultAbort, true);
// updateOomAdjLocked() will be done here
trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
@@ -15106,30 +15119,13 @@
// LIFETIME MANAGEMENT
// =========================================================
- // Returns whether the app is receiving broadcast.
- // If receiving, fetch all broadcast queues which the app is
- // the current [or imminent] receiver on.
- boolean isReceivingBroadcastLocked(ProcessRecord app,
- ArraySet<BroadcastQueue> receivingQueues) {
- final ProcessReceiverRecord prr = app.mReceivers;
- final int numOfReceivers = prr.numberOfCurReceivers();
- if (numOfReceivers > 0) {
- for (int i = 0; i < numOfReceivers; i++) {
- receivingQueues.add(prr.getCurReceiverAt(i).queue);
- }
- return true;
- }
-
- // It's not the current receiver, but it might be starting up to become one
+ boolean isReceivingBroadcastLocked(ProcessRecord app, int[] outSchedGroup) {
+ int res = ProcessList.SCHED_GROUP_UNDEFINED;
for (BroadcastQueue queue : mBroadcastQueues) {
- final BroadcastRecord r = queue.getPendingBroadcastLocked();
- if (r != null && r.curApp == app) {
- // found it; report which queue it's in
- receivingQueues.add(queue);
- }
+ res = Math.max(res, queue.getPreferredSchedulingGroupLocked(app));
}
-
- return !receivingQueues.isEmpty();
+ outSchedGroup[0] = res;
+ return res != ProcessList.SCHED_GROUP_UNDEFINED;
}
Association startAssociationLocked(int sourceUid, String sourceProcess, int sourceState,
@@ -15237,7 +15233,7 @@
@GuardedBy("this")
final boolean canGcNowLocked() {
for (BroadcastQueue q : mBroadcastQueues) {
- if (!q.isIdle()) {
+ if (!q.isIdleLocked()) {
return false;
}
}
@@ -17814,35 +17810,19 @@
public void waitForBroadcastIdle(@Nullable PrintWriter pw) {
enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
- while (true) {
- boolean idle = true;
- synchronized (this) {
- for (BroadcastQueue queue : mBroadcastQueues) {
- if (!queue.isIdle()) {
- final String msg = "Waiting for queue " + queue + " to become idle...";
- if (pw != null) {
- pw.println(msg);
- pw.println(queue.describeState());
- pw.flush();
- }
- Slog.v(TAG, msg);
- queue.flush();
- idle = false;
- }
- }
- }
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ queue.waitForIdle(pw);
+ }
+ if (pw != null) {
+ pw.println("All broadcast queues are idle!");
+ pw.flush();
+ }
+ }
- if (idle) {
- final String msg = "All broadcast queues are idle!";
- if (pw != null) {
- pw.println(msg);
- pw.flush();
- }
- Slog.v(TAG, msg);
- return;
- } else {
- SystemClock.sleep(1000);
- }
+ public void waitForBroadcastBarrier(@Nullable PrintWriter pw) {
+ enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ queue.waitForBarrier(pw);
}
}
@@ -17903,10 +17883,11 @@
}
/**
- * Reset the dropbox rate limiter
+ * Reset the dropbox rate limiter here and in BootReceiver
*/
void resetDropboxRateLimiter() {
mDropboxRateLimiter.reset();
+ BootReceiver.resetDropboxRateLimiter();
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a42b2a4..b4f6e35 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -341,6 +341,8 @@
return runNoHomeScreen(pw);
case "wait-for-broadcast-idle":
return runWaitForBroadcastIdle(pw);
+ case "wait-for-broadcast-barrier":
+ return runWaitForBroadcastBarrier(pw);
case "compat":
return runCompat(pw);
case "refresh-settings-cache":
@@ -3112,6 +3114,11 @@
return 0;
}
+ int runWaitForBroadcastBarrier(PrintWriter pw) throws RemoteException {
+ mInternal.waitForBroadcastBarrier(pw);
+ return 0;
+ }
+
int runRefreshSettingsCache() throws RemoteException {
mInternal.refreshSettingsCache();
return 0;
diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java
new file mode 100644
index 0000000..d820d6c
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastHistory.java
@@ -0,0 +1,224 @@
+/*
+ * 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.am;
+
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Collection of recent historical broadcasts that are available to be dumped
+ * for debugging purposes. Automatically trims itself over time.
+ */
+public class BroadcastHistory {
+ static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
+ static final int MAX_BROADCAST_SUMMARY_HISTORY
+ = ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
+
+ /**
+ * Historical data of past broadcasts, for debugging. This is a ring buffer
+ * whose last element is at mHistoryNext.
+ */
+ final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY];
+ int mHistoryNext = 0;
+
+ /**
+ * Summary of historical data of past broadcasts, for debugging. This is a
+ * ring buffer whose last element is at mSummaryHistoryNext.
+ */
+ final Intent[] mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
+ int mSummaryHistoryNext = 0;
+
+ /**
+ * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring
+ * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall
+ * clock time, not elapsed.
+ */
+ final long[] mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+ final long[] mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+ final long[] mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+
+ public void addBroadcastToHistoryLocked(BroadcastRecord original) {
+ // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords,
+ // So don't change the incoming record directly.
+ final BroadcastRecord historyRecord = original.maybeStripForHistory();
+
+ mBroadcastHistory[mHistoryNext] = historyRecord;
+ mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY);
+
+ mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent;
+ mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime;
+ mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime;
+ mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis();
+ mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY);
+ }
+
+ private final int ringAdvance(int x, final int increment, final int ringSize) {
+ x += increment;
+ if (x < 0) return (ringSize - 1);
+ else if (x >= ringSize) return 0;
+ else return x;
+ }
+
+ public void dumpDebug(ProtoOutputStream proto) {
+ int lastIndex = mHistoryNext;
+ int ringIndex = lastIndex;
+ do {
+ // increasing index = more recent entry, and we want to print the most
+ // recent first and work backwards, so we roll through the ring backwards.
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r != null) {
+ r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS);
+ }
+ } while (ringIndex != lastIndex);
+
+ lastIndex = ringIndex = mSummaryHistoryNext;
+ do {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ Intent intent = mBroadcastSummaryHistory[ringIndex];
+ if (intent == null) {
+ continue;
+ }
+ long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY);
+ intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT,
+ false, true, true, false);
+ proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS,
+ mSummaryHistoryEnqueueTime[ringIndex]);
+ proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS,
+ mSummaryHistoryDispatchTime[ringIndex]);
+ proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS,
+ mSummaryHistoryFinishTime[ringIndex]);
+ proto.end(summaryToken);
+ } while (ringIndex != lastIndex);
+ }
+
+ public boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName,
+ SimpleDateFormat sdf, boolean dumpAll, boolean needSep) {
+ int i;
+ boolean printed = false;
+
+ i = -1;
+ int lastIndex = mHistoryNext;
+ int ringIndex = lastIndex;
+ do {
+ // increasing index = more recent entry, and we want to print the most
+ // recent first and work backwards, so we roll through the ring backwards.
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r == null) {
+ continue;
+ }
+
+ i++; // genuine record of some sort even if we're filtering it out
+ if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.println(" Historical broadcasts [" + queueName + "]:");
+ printed = true;
+ }
+ if (dumpAll) {
+ pw.print(" Historical Broadcast " + queueName + " #");
+ pw.print(i); pw.println(":");
+ r.dump(pw, " ", sdf);
+ } else {
+ pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
+ pw.print(" ");
+ pw.println(r.intent.toShortString(false, true, true, false));
+ if (r.targetComp != null && r.targetComp != r.intent.getComponent()) {
+ pw.print(" targetComp: "); pw.println(r.targetComp.toShortString());
+ }
+ Bundle bundle = r.intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: "); pw.println(bundle.toString());
+ }
+ }
+ } while (ringIndex != lastIndex);
+
+ if (dumpPackage == null) {
+ lastIndex = ringIndex = mSummaryHistoryNext;
+ if (dumpAll) {
+ printed = false;
+ i = -1;
+ } else {
+ // roll over the 'i' full dumps that have already been issued
+ for (int j = i;
+ j > 0 && ringIndex != lastIndex;) {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r == null) {
+ continue;
+ }
+ j--;
+ }
+ }
+ // done skipping; dump the remainder of the ring. 'i' is still the ordinal within
+ // the overall broadcast history.
+ do {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ Intent intent = mBroadcastSummaryHistory[ringIndex];
+ if (intent == null) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.println(" Historical broadcasts summary [" + queueName + "]:");
+ printed = true;
+ }
+ if (!dumpAll && i >= 50) {
+ pw.println(" ...");
+ break;
+ }
+ i++;
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ pw.println(intent.toShortString(false, true, true, false));
+ pw.print(" ");
+ TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
+ - mSummaryHistoryEnqueueTime[ringIndex], pw);
+ pw.print(" dispatch ");
+ TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
+ - mSummaryHistoryDispatchTime[ringIndex], pw);
+ pw.println(" finish");
+ pw.print(" enq=");
+ pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
+ pw.print(" disp=");
+ pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
+ pw.print(" fin=");
+ pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: "); pw.println(bundle.toString());
+ }
+ } while (ringIndex != lastIndex);
+ }
+ return needSep;
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 752c6b6..6814509 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -18,12 +18,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -43,16 +41,18 @@
final @NonNull Handler mHandler;
final @NonNull BroadcastConstants mConstants;
final @NonNull BroadcastSkipPolicy mSkipPolicy;
+ final @NonNull BroadcastHistory mHistory;
final @NonNull String mQueueName;
BroadcastQueue(@NonNull ActivityManagerService service, @NonNull Handler handler,
@NonNull String name, @NonNull BroadcastConstants constants,
- @NonNull BroadcastSkipPolicy skipPolicy) {
+ @NonNull BroadcastSkipPolicy skipPolicy, @NonNull BroadcastHistory history) {
mService = Objects.requireNonNull(service);
mHandler = Objects.requireNonNull(handler);
mQueueName = Objects.requireNonNull(name);
mConstants = Objects.requireNonNull(constants);
mSkipPolicy = Objects.requireNonNull(skipPolicy);
+ mHistory = Objects.requireNonNull(history);
}
void start(@NonNull ContentResolver resolver) {
@@ -66,11 +66,15 @@
public abstract boolean isDelayBehindServices();
- @GuardedBy("mService")
- public abstract @Nullable BroadcastRecord getPendingBroadcastLocked();
-
- @GuardedBy("mService")
- public abstract @Nullable BroadcastRecord getActiveBroadcastLocked();
+ /**
+ * Return the preferred scheduling group for the given process, typically
+ * influenced by a broadcast being actively dispatched.
+ *
+ * @return scheduling group such as {@link ProcessList#SCHED_GROUP_DEFAULT},
+ * otherwise {@link ProcessList#SCHED_GROUP_UNDEFINED} if this queue
+ * has no opinion.
+ */
+ public abstract int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app);
/**
* Enqueue the given broadcast to be eventually dispatched.
@@ -85,17 +89,14 @@
public abstract void enqueueBroadcastLocked(@NonNull BroadcastRecord r);
/**
- * Signal delivered back from a {@link BroadcastReceiver} to indicate that
- * it's finished processing the current broadcast being dispatched to it.
+ * Signal delivered back from the given process to indicate that it's
+ * finished processing the current broadcast being dispatched to it.
* <p>
* If this signal isn't delivered back in a timely fashion, we assume the
* receiver has somehow wedged and we trigger an ANR.
- *
- * @param receiver the value to match against
- * {@link BroadcastRecord#receiver} to identify the caller.
*/
@GuardedBy("mService")
- public abstract boolean finishReceiverLocked(@NonNull IBinder receiver, int resultCode,
+ public abstract boolean finishReceiverLocked(@NonNull ProcessRecord app, int resultCode,
@Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,
boolean waitForServices);
@@ -144,22 +145,40 @@
* Quickly determine if this queue has broadcasts that are still waiting to
* be delivered at some point in the future.
*
- * @see #flush()
+ * @see #waitForIdle
+ * @see #waitForBarrier
*/
- public abstract boolean isIdle();
+ @GuardedBy("mService")
+ public abstract boolean isIdleLocked();
+
+ /**
+ * Wait until this queue becomes completely idle.
+ * <p>
+ * Any broadcasts waiting to be delivered at some point in the future will
+ * be dispatched as quickly as possible.
+ * <p>
+ * Callers are cautioned that the queue may take a long time to go idle,
+ * since running apps can continue sending new broadcasts in perpetuity;
+ * consider using {@link #waitForBarrier} instead.
+ */
+ public abstract void waitForIdle(@Nullable PrintWriter pw);
+
+ /**
+ * Wait until any currently waiting broadcasts have been dispatched.
+ * <p>
+ * Any broadcasts waiting to be delivered at some point in the future will
+ * be dispatched as quickly as possible.
+ * <p>
+ * Callers are advised that this method will <em>not</em> wait for any
+ * future broadcasts that are newly enqueued after being invoked.
+ */
+ public abstract void waitForBarrier(@Nullable PrintWriter pw);
/**
* Brief summary of internal state, useful for debugging purposes.
*/
- public abstract @NonNull String describeState();
-
- /**
- * Flush any broadcasts still waiting to be delivered, causing them to be
- * delivered as soon as possible.
- *
- * @see #isIdle()
- */
- public abstract void flush();
+ @GuardedBy("mService")
+ public abstract @NonNull String describeStateLocked();
public abstract void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index d914612..56112cf 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -73,7 +73,6 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseIntArray;
-import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.TimeoutRecord;
@@ -85,8 +84,8 @@
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Date;
import java.util.Set;
+import java.util.function.BooleanSupplier;
/**
* BROADCASTS
@@ -99,16 +98,14 @@
private static final String TAG_MU = TAG + POSTFIX_MU;
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
- static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
- static final int MAX_BROADCAST_SUMMARY_HISTORY
- = ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
-
/**
* If true, we can delay broadcasts while waiting services to finish in the previous
* receiver's process.
*/
final boolean mDelayBehindServices;
+ final int mSchedGroup;
+
/**
* Lists of all active broadcasts that are to be executed immediately
* (without waiting for another broadcast to finish). Currently this only
@@ -133,29 +130,6 @@
private int mNextToken = 0;
/**
- * Historical data of past broadcasts, for debugging. This is a ring buffer
- * whose last element is at mHistoryNext.
- */
- final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY];
- int mHistoryNext = 0;
-
- /**
- * Summary of historical data of past broadcasts, for debugging. This is a
- * ring buffer whose last element is at mSummaryHistoryNext.
- */
- final Intent[] mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
- int mSummaryHistoryNext = 0;
-
- /**
- * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring
- * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall
- * clock time, not elapsed.
- */
- final long[] mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
- final long[] mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
- final long[] mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
-
- /**
* Set when we current have a BROADCAST_INTENT_MSG in flight.
*/
boolean mBroadcastsScheduled = false;
@@ -211,17 +185,19 @@
}
BroadcastQueueImpl(ActivityManagerService service, Handler handler,
- String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
+ String name, BroadcastConstants constants, boolean allowDelayBehindServices,
+ int schedGroup) {
this(service, handler, name, constants, new BroadcastSkipPolicy(service),
- allowDelayBehindServices);
+ new BroadcastHistory(), allowDelayBehindServices, schedGroup);
}
BroadcastQueueImpl(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, BroadcastSkipPolicy skipPolicy,
- boolean allowDelayBehindServices) {
- super(service, handler, name, constants, skipPolicy);
+ BroadcastHistory history, boolean allowDelayBehindServices, int schedGroup) {
+ super(service, handler, name, constants, skipPolicy, history);
mHandler = new BroadcastHandler(handler.getLooper());
mDelayBehindServices = allowDelayBehindServices;
+ mSchedGroup = schedGroup;
mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
}
@@ -242,6 +218,18 @@
return mDispatcher.getActiveBroadcastLocked();
}
+ public int getPreferredSchedulingGroupLocked(ProcessRecord app) {
+ final BroadcastRecord active = getActiveBroadcastLocked();
+ if (active != null && active.curApp == app) {
+ return mSchedGroup;
+ }
+ final BroadcastRecord pending = getPendingBroadcastLocked();
+ if (pending != null && pending.curApp == app) {
+ return mSchedGroup;
+ }
+ return ProcessList.SCHED_GROUP_UNDEFINED;
+ }
+
public void enqueueBroadcastLocked(BroadcastRecord r) {
final boolean replacePending = (r.intent.getFlags()
& Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
@@ -369,7 +357,6 @@
return;
}
- r.receiver = thread.asBinder();
r.curApp = app;
final ProcessReceiverRecord prr = app.mReceivers;
prr.addCurReceiver(r);
@@ -407,7 +394,6 @@
if (!started) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + ": NOT STARTED!");
- r.receiver = null;
r.curApp = null;
prr.removeCurReceiver(r);
}
@@ -524,16 +510,16 @@
mBroadcastsScheduled = true;
}
- public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
+ public BroadcastRecord getMatchingOrderedReceiver(ProcessRecord app) {
BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
if (br == null) {
Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
+ "] no active broadcast");
return null;
}
- if (br.receiver != receiver) {
+ if (br.curApp != app) {
Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
- + "] active broadcast " + br.receiver + " doesn't match " + receiver);
+ + "] active broadcast " + br.curApp + " doesn't match " + app);
return null;
}
return br;
@@ -564,9 +550,9 @@
}, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
}
- public boolean finishReceiverLocked(IBinder receiver, int resultCode,
+ public boolean finishReceiverLocked(ProcessRecord app, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
- final BroadcastRecord r = getMatchingOrderedReceiver(receiver);
+ final BroadcastRecord r = getMatchingOrderedReceiver(app);
if (r != null) {
return finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, waitForServices);
@@ -647,7 +633,6 @@
}
}
- r.receiver = null;
r.intent.setComponent(null);
if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
r.curApp.mReceivers.removeCurReceiver(r);
@@ -674,7 +659,7 @@
// If we want to wait behind services *AND* we're finishing the head/
// active broadcast on its queue
if (waitForServices && r.curComponent != null && r.queue.isDelayBehindServices()
- && r.queue.getActiveBroadcastLocked() == r) {
+ && ((BroadcastQueueImpl) r.queue).getActiveBroadcastLocked() == r) {
ActivityInfo nextReceiver;
if (r.nextReceiver < r.receivers.size()) {
Object obj = r.receivers.get(r.nextReceiver);
@@ -805,7 +790,6 @@
// don't want to touch the fields that keep track of the current
// state of ordered broadcasts.
if (ordered) {
- r.receiver = filter.receiverList.receiver.asBinder();
r.curFilter = filter;
filter.receiverList.curBroadcast = r;
r.state = BroadcastRecord.CALL_IN_RECEIVE;
@@ -869,7 +853,6 @@
}
// And BroadcastRecord state related to ordered delivery, if appropriate
if (ordered) {
- r.receiver = null;
r.curFilter = null;
filter.receiverList.curBroadcast = null;
}
@@ -1289,12 +1272,13 @@
+ filter + ": " + r);
r.mIsReceiverAppRunning = true;
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
- if (r.receiver == null || !r.ordered) {
+ if ((r.curReceiver == null && r.curFilter == null) || !r.ordered) {
// The receiver has already finished, so schedule to
// process the next one.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
- + mQueueName + "]: ordered="
- + r.ordered + " receiver=" + r.receiver);
+ + mQueueName + "]: ordered=" + r.ordered
+ + " curFilter=" + r.curFilter
+ + " curReceiver=" + r.curReceiver);
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
} else {
@@ -1346,7 +1330,6 @@
"Skipping delivery of ordered [" + mQueueName + "] "
+ r + " for reason described above");
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
- r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
r.manifestSkipCount++;
@@ -1636,8 +1619,8 @@
final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
long timeoutDurationMs = now - r.receiverTime;
- Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
- + ", started " + timeoutDurationMs + "ms ago");
+ Slog.w(TAG, "Timeout of broadcast " + r + " - curFilter=" + r.curFilter + " curReceiver="
+ + r.curReceiver + ", started " + timeoutDurationMs + "ms ago");
r.receiverTime = now;
if (!debugging) {
r.anrCount++;
@@ -1689,13 +1672,6 @@
}
}
- private final int ringAdvance(int x, final int increment, final int ringSize) {
- x += increment;
- if (x < 0) return (ringSize - 1);
- else if (x >= ringSize) return 0;
- else return x;
- }
-
private final void addBroadcastToHistoryLocked(BroadcastRecord original) {
if (original.callingUid < 0) {
// This was from a registerReceiver() call; ignore it.
@@ -1716,18 +1692,7 @@
original.callingUid, 0, callerPackage).sendToTarget();
}
- // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords,
- // So don't change the incoming record directly.
- final BroadcastRecord historyRecord = original.maybeStripForHistory();
-
- mBroadcastHistory[mHistoryNext] = historyRecord;
- mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY);
-
- mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent;
- mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime;
- mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime;
- mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis();
- mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY);
+ mHistory.addBroadcastToHistoryLocked(original);
}
public boolean cleanupDisabledPackageReceiversLocked(
@@ -1781,13 +1746,72 @@
record.intent == null ? "" : record.intent.getAction());
}
- public boolean isIdle() {
+ public boolean isIdleLocked() {
return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle()
&& (mPendingBroadcast == null);
}
- public void flush() {
- cancelDeferrals();
+ public boolean isBeyondBarrierLocked(long barrierTime) {
+ // If nothing active, we're beyond barrier
+ if (isIdleLocked()) return true;
+
+ // Check if active broadcast is beyond barrier
+ final BroadcastRecord active = getActiveBroadcastLocked();
+ if (active != null && active.enqueueTime > barrierTime) {
+ return true;
+ }
+
+ // Check if pending broadcast is beyond barrier
+ final BroadcastRecord pending = getPendingBroadcastLocked();
+ if (pending != null && pending.enqueueTime > barrierTime) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public void waitForIdle(PrintWriter pw) {
+ waitFor(() -> isIdleLocked(), pw, "idle");
+ }
+
+ public void waitForBarrier(PrintWriter pw) {
+ final long barrierTime = SystemClock.uptimeMillis();
+ waitFor(() -> isBeyondBarrierLocked(barrierTime), pw, "barrier");
+ }
+
+ private void waitFor(BooleanSupplier condition, PrintWriter pw, String conditionName) {
+ long lastPrint = 0;
+ while (true) {
+ synchronized (mService) {
+ if (condition.getAsBoolean()) {
+ final String msg = "Queue [" + mQueueName + "] reached " + conditionName
+ + " condition";
+ Slog.v(TAG, msg);
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
+ return;
+ }
+ }
+
+ // Print at most every second
+ final long now = SystemClock.uptimeMillis();
+ if (now >= lastPrint + 1000) {
+ lastPrint = now;
+ final String msg = "Queue [" + mQueueName + "] waiting for " + conditionName
+ + " condition; state is " + describeStateLocked();
+ Slog.v(TAG, msg);
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
+ }
+
+ // Push through any deferrals to try meeting our condition
+ cancelDeferrals();
+ SystemClock.sleep(100);
+ }
}
// Used by wait-for-broadcast-idle : fast-forward all current deferrals to
@@ -1799,11 +1823,9 @@
}
}
- public String describeState() {
- synchronized (mService) {
- return mParallelBroadcasts.size() + " parallel; "
- + mDispatcher.describeStateLocked();
- }
+ public String describeStateLocked() {
+ return mParallelBroadcasts.size() + " parallel; "
+ + mDispatcher.describeStateLocked();
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -1818,37 +1840,7 @@
if (mPendingBroadcast != null) {
mPendingBroadcast.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCAST);
}
-
- int lastIndex = mHistoryNext;
- int ringIndex = lastIndex;
- do {
- // increasing index = more recent entry, and we want to print the most
- // recent first and work backwards, so we roll through the ring backwards.
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r != null) {
- r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS);
- }
- } while (ringIndex != lastIndex);
-
- lastIndex = ringIndex = mSummaryHistoryNext;
- do {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- Intent intent = mBroadcastSummaryHistory[ringIndex];
- if (intent == null) {
- continue;
- }
- long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY);
- intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT,
- false, true, true, false);
- proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS,
- mSummaryHistoryEnqueueTime[ringIndex]);
- proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS,
- mSummaryHistoryDispatchTime[ringIndex]);
- proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS,
- mSummaryHistoryFinishTime[ringIndex]);
- proto.end(summaryToken);
- } while (ringIndex != lastIndex);
+ mHistory.dumpDebug(proto);
proto.end(token);
}
@@ -1889,114 +1881,8 @@
needSep = true;
}
}
-
mConstants.dump(pw);
-
- int i;
- boolean printed = false;
-
- i = -1;
- int lastIndex = mHistoryNext;
- int ringIndex = lastIndex;
- do {
- // increasing index = more recent entry, and we want to print the most
- // recent first and work backwards, so we roll through the ring backwards.
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r == null) {
- continue;
- }
-
- i++; // genuine record of some sort even if we're filtering it out
- if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- pw.println(" Historical broadcasts [" + mQueueName + "]:");
- printed = true;
- }
- if (dumpAll) {
- pw.print(" Historical Broadcast " + mQueueName + " #");
- pw.print(i); pw.println(":");
- r.dump(pw, " ", sdf);
- } else {
- pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
- pw.print(" ");
- pw.println(r.intent.toShortString(false, true, true, false));
- if (r.targetComp != null && r.targetComp != r.intent.getComponent()) {
- pw.print(" targetComp: "); pw.println(r.targetComp.toShortString());
- }
- Bundle bundle = r.intent.getExtras();
- if (bundle != null) {
- pw.print(" extras: "); pw.println(bundle.toString());
- }
- }
- } while (ringIndex != lastIndex);
-
- if (dumpPackage == null) {
- lastIndex = ringIndex = mSummaryHistoryNext;
- if (dumpAll) {
- printed = false;
- i = -1;
- } else {
- // roll over the 'i' full dumps that have already been issued
- for (int j = i;
- j > 0 && ringIndex != lastIndex;) {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r == null) {
- continue;
- }
- j--;
- }
- }
- // done skipping; dump the remainder of the ring. 'i' is still the ordinal within
- // the overall broadcast history.
- do {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- Intent intent = mBroadcastSummaryHistory[ringIndex];
- if (intent == null) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- pw.println(" Historical broadcasts summary [" + mQueueName + "]:");
- printed = true;
- }
- if (!dumpAll && i >= 50) {
- pw.println(" ...");
- break;
- }
- i++;
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(intent.toShortString(false, true, true, false));
- pw.print(" ");
- TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
- - mSummaryHistoryEnqueueTime[ringIndex], pw);
- pw.print(" dispatch ");
- TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
- - mSummaryHistoryDispatchTime[ringIndex], pw);
- pw.println(" finish");
- pw.print(" enq=");
- pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
- pw.print(" disp=");
- pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
- pw.print(" fin=");
- pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
- Bundle bundle = intent.getExtras();
- if (bundle != null) {
- pw.print(" extras: "); pw.println(bundle.toString());
- }
- } while (ringIndex != lastIndex);
- }
-
+ needSep = mHistory.dumpLocked(pw, dumpPackage, mQueueName, sdf, dumpAll, needSep);
return needSep;
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 18fbfde..817831c 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -103,7 +103,6 @@
Bundle resultExtras; // current result extra data values.
boolean resultAbort; // current result abortBroadcast value.
int nextReceiver; // next receiver to be executed.
- IBinder receiver; // who is currently running, null if none.
int state;
int anrCount; // has this broadcast record hit any ANRs?
int manifestCount; // number of manifest receivers dispatched.
@@ -133,15 +132,10 @@
static final int DELIVERY_SKIPPED = 2;
static final int DELIVERY_TIMEOUT = 3;
- // The following are set when we are calling a receiver (one that
- // was found in our list of registered receivers).
- BroadcastFilter curFilter;
-
- // The following are set only when we are launching a receiver (one
- // that was found by querying the package manager).
ProcessRecord curApp; // hosting application of current receiver.
ComponentName curComponent; // the receiver class that is currently running.
- ActivityInfo curReceiver; // info about the receiver that is currently running.
+ ActivityInfo curReceiver; // the manifest receiver that is currently running.
+ BroadcastFilter curFilter; // the registered receiver currently running.
Bundle curFilteredExtras; // the bundle that has been filtered by the package visibility rules
boolean mIsReceiverAppRunning; // Was the receiver's app already running.
@@ -217,9 +211,8 @@
pw.print(" sticky="); pw.print(sticky);
pw.print(" initialSticky="); pw.println(initialSticky);
}
- if (nextReceiver != 0 || receiver != null) {
+ if (nextReceiver != 0) {
pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver);
- pw.print(" receiver="); pw.println(receiver);
}
if (curFilter != null) {
pw.print(prefix); pw.print("curFilter="); pw.println(curFilter);
@@ -368,7 +361,6 @@
resultExtras = from.resultExtras;
resultAbort = from.resultAbort;
nextReceiver = from.nextReceiver;
- receiver = from.receiver;
state = from.state;
anrCount = from.anrCount;
manifestCount = from.manifestCount;
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 55fc6c5..4574302 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -40,18 +40,19 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.ProcLocksReader;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
+
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -109,6 +110,7 @@
private static final int COMPACT_ACTION_ANON_FLAG = 2;
private static final String ATRACE_COMPACTION_TRACK = "Compaction";
+ private static final String ATRACE_FREEZER_TRACK = "Freezer";
// Defaults for phenotype flags.
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
@@ -1309,6 +1311,7 @@
}
try {
+ traceAppFreeze(app.processName, pid, false);
Process.setProcessFrozen(pid, app.uid, false);
opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
@@ -1359,6 +1362,7 @@
}
try {
+ traceAppFreeze(app.processName, pid, false);
Process.setProcessFrozen(pid, app.uid, false);
} catch (Exception e) {
Slog.e(TAG_AM, "Unable to quick unfreeze " + pid);
@@ -1366,6 +1370,11 @@
}
}
+ private static void traceAppFreeze(String processName, int pid, boolean freeze) {
+ Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_FREEZER_TRACK,
+ (freeze ? "Freeze " : "Unfreeze ") + processName + ":" + pid);
+ }
+
/**
* To be called when the given app is killed.
*/
@@ -1959,6 +1968,7 @@
long unfreezeTime = opt.getFreezeUnfreezeTime();
try {
+ traceAppFreeze(proc.processName, pid, true);
Process.setProcessFrozen(pid, proc.uid, true);
opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 78629b0..a36c298 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -29,7 +29,6 @@
import static com.android.server.am.ActivityManagerService.TAG_MU;
import static com.android.server.am.ProcessProfileRecord.HOSTING_COMPONENT_TYPE_PROVIDER;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -49,6 +48,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
@@ -960,22 +960,20 @@
String getProviderMimeType(Uri uri, int userId) {
mService.enforceNotIsolatedCaller("getProviderMimeType");
final String name = uri.getAuthority();
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
- final long ident = canClearIdentity(callingPid, callingUid, safeUserId)
- ? Binder.clearCallingIdentity() : 0;
- final ContentProviderHolder holder;
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ long ident = 0;
+ boolean clearedIdentity = false;
+ userId = mService.mUserController.unsafeConvertIncomingUser(userId);
+ if (canClearIdentity(callingPid, callingUid, userId)) {
+ clearedIdentity = true;
+ ident = Binder.clearCallingIdentity();
+ }
+ ContentProviderHolder holder = null;
try {
holder = getContentProviderExternalUnchecked(name, null, callingUid,
- "*getmimetype*", safeUserId);
- } finally {
- if (ident != 0) {
- Binder.restoreCallingIdentity(ident);
- }
- }
- try {
- if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) {
+ "*getmimetype*", userId);
+ if (holder != null) {
final IBinder providerConnection = holder.connection;
final ComponentName providerName = holder.info.getComponentName();
// Note: creating a new Runnable instead of using a lambda here since lambdas in
@@ -1004,13 +1002,15 @@
return null;
} finally {
// We need to clear the identity to call removeContentProviderExternalUnchecked
- final long token = Binder.clearCallingIdentity();
+ if (!clearedIdentity) {
+ ident = Binder.clearCallingIdentity();
+ }
try {
if (holder != null) {
- removeContentProviderExternalUnchecked(name, null /* token */, safeUserId);
+ removeContentProviderExternalUnchecked(name, null, userId);
}
} finally {
- Binder.restoreCallingIdentity(token);
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -1029,20 +1029,12 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
- final long ident = canClearIdentity(callingPid, callingUid, safeUserId)
+ final long ident = canClearIdentity(callingPid, callingUid, userId)
? Binder.clearCallingIdentity() : 0;
- final ContentProviderHolder holder;
try {
- holder = getContentProviderExternalUnchecked(name, null /* token */,
+ final ContentProviderHolder holder = getContentProviderExternalUnchecked(name, null,
callingUid, "*getmimetype*", safeUserId);
- } finally {
- if (ident != 0) {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- try {
- if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) {
+ if (holder != null) {
holder.provider.getTypeAsync(uri, new RemoteCallback(result -> {
final long identity = Binder.clearCallingIdentity();
try {
@@ -1058,6 +1050,8 @@
} catch (RemoteException e) {
Log.w(TAG, "Content provider dead retrieving " + uri, e);
resultCallback.sendResult(Bundle.EMPTY);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -1073,16 +1067,6 @@
callingUid, -1, true) == PackageManager.PERMISSION_GRANTED;
}
- private boolean isHolderVisibleToCaller(@Nullable ContentProviderHolder holder, int callingUid,
- @UserIdInt int userId) {
- if (holder == null || holder.info == null) {
- return false;
- }
-
- return !mService.getPackageManagerInternal()
- .filterAppAccess(holder.info.packageName, callingUid, userId);
- }
-
/**
* Check if the calling UID has a possible chance at accessing the provider
* at the given authority and user.
@@ -1151,7 +1135,9 @@
"*checkContentProviderUriPermission*", userId);
if (holder != null) {
- final AndroidPackage androidPackage = mService.getPackageManagerInternal()
+ final PackageManagerInternal packageManagerInt = LocalServices.getService(
+ PackageManagerInternal.class);
+ final AndroidPackage androidPackage = packageManagerInt
.getPackage(Binder.getCallingUid());
if (androidPackage == null) {
return PackageManager.PERMISSION_DENIED;
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 6087f76..e5975c3 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -108,7 +108,7 @@
}
/** Resets the rate limiter memory. */
- void reset() {
+ public void reset() {
synchronized (mErrorClusterRecords) {
mErrorClusterRecords.clear();
}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 15887f0..da209f0 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -37,3 +37,7 @@
per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, jsharkey@google.com, yamasani@google.com
+
+# Multiuser
+per-file User* = file:/MULTIUSER_OWNERS
+
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 12aa66b..80dd266 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -305,7 +305,7 @@
*/
private final Handler mProcessGroupHandler;
- private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
+ private final int[] mTmpSchedGroup = new int[1];
private final ActivityManagerService mService;
private final ProcessList mProcessList;
@@ -1677,14 +1677,13 @@
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
}
- } else if (state.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {
+ } else if (state.getCachedIsReceivingBroadcast(mTmpSchedGroup)) {
// An app that is currently receiving a broadcast also
// counts as being in the foreground for OOM killer purposes.
// It's placed in a sched group based on the nature of the
// broadcast as reflected by which queue it's active in.
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue))
- ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+ schedGroup = mTmpSchedGroup[0];
state.setAdjType("broadcast");
procState = ActivityManager.PROCESS_STATE_RECEIVER;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 48eb0de..affb084 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -276,6 +276,8 @@
// Memory pages are 4K.
static final int PAGE_SIZE = 4 * 1024;
+ // Activity manager's version of an undefined schedule group
+ static final int SCHED_GROUP_UNDEFINED = Integer.MIN_VALUE;
// Activity manager's version of Process.THREAD_GROUP_BACKGROUND
static final int SCHED_GROUP_BACKGROUND = 0;
// Activity manager's version of Process.THREAD_GROUP_RESTRICTED
@@ -813,12 +815,14 @@
< LmkdStatsReporter.KILL_OCCURRED_MSG_SIZE) {
return false;
}
- Pair<Integer, Integer> temp = getNumForegroundServices();
- final int totalForegroundServices = temp.first;
- final int procsWithForegroundServices = temp.second;
+ // Note: directly access
+ // ActiveServices.sNumForegroundServices, do not try to
+ // hold AMS lock here, otherwise it is a potential deadlock.
+ Pair<Integer, Integer> foregroundServices =
+ ActiveServices.sNumForegroundServices.get();
LmkdStatsReporter.logKillOccurred(inputData,
- totalForegroundServices,
- procsWithForegroundServices);
+ foregroundServices.first,
+ foregroundServices.second);
return true;
case LMK_STATE_CHANGED:
if (receivedLen
@@ -3640,7 +3644,14 @@
if (thread == null) {
return null;
}
- final IBinder threadBinder = thread.asBinder();
+ return getLRURecordForAppLOSP(thread.asBinder());
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ProcessRecord getLRURecordForAppLOSP(IBinder threadBinder) {
+ if (threadBinder == null) {
+ return null;
+ }
// Find the application record.
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord rec = mLruProcesses.get(i);
@@ -3724,6 +3735,10 @@
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
app.getPid(), app.getPackageList());
+ if (app.getPkgDeps() != null) {
+ final int size = app.getPkgDeps().size();
+ currApp.pkgDeps = app.getPkgDeps().toArray(new String[size]);
+ }
fillInProcMemInfoLOSP(app, currApp, clientTargetSdk);
if (state.getAdjSource() instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord) state.getAdjSource()).getPid();
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index eb1fd3a..ef13778 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -30,7 +30,6 @@
import android.app.ActivityManager;
import android.content.ComponentName;
import android.os.SystemClock;
-import android.util.ArraySet;
import android.util.Slog;
import android.util.TimeUtils;
@@ -1071,14 +1070,12 @@
}
@GuardedBy("mService")
- boolean getCachedIsReceivingBroadcast(ArraySet<BroadcastQueue> tmpQueue) {
+ boolean getCachedIsReceivingBroadcast(int[] outSchedGroup) {
if (mCachedIsReceivingBroadcast == VALUE_INVALID) {
- tmpQueue.clear();
- mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(mApp, tmpQueue)
+ mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(mApp, outSchedGroup)
? VALUE_TRUE : VALUE_FALSE;
if (mCachedIsReceivingBroadcast == VALUE_TRUE) {
- mCachedSchedGroup = tmpQueue.contains(mService.mFgBroadcastQueue)
- ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+ mCachedSchedGroup = outSchedGroup[0];
mApp.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_BROADCAST_RECEIVER);
} else {
mApp.mProfile.clearHostingComponentType(HOSTING_COMPONENT_TYPE_BROADCAST_RECEIVER);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a1f3dae..043da55 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1036,7 +1036,13 @@
if (uss.state != UserState.STATE_STOPPING
&& uss.state != UserState.STATE_SHUTDOWN) {
uss.setState(UserState.STATE_STOPPING);
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
+ userManagerInternal.setUserState(userId, uss.state);
+ // TODO(b/239982558): for now we're just updating the user's visibility, but most likely
+ // we'll need to remove this call and handle that as part of the user state workflow
+ // instead.
+ userManagerInternal.unassignUserFromDisplay(userId);
+
updateStartedUserArrayLU();
final boolean allowDelayedLockingCopied = allowDelayedLocking;
@@ -1076,12 +1082,6 @@
Binder.getCallingPid(), UserHandle.USER_ALL);
});
}
-
- // TODO(b/239982558): for now we're just updating the user's visibility, but most likely
- // we'll need to remove this call and handle that as part of the user state workflow
- // instead.
- // TODO(b/240613396) also check if multi-display is supported
- mInjector.getUserManagerInternal().assignUserToDisplay(userId, Display.INVALID_DISPLAY);
}
private void finishUserStopping(final int userId, final UserState uss,
@@ -1391,7 +1391,6 @@
&& !user.isQuietModeEnabled();
}
- // TODO(b/239982558): might need to infer the display id based on parent user
/**
* Starts a user only if it's a profile, with a more relaxed permission requirement:
* {@link android.Manifest.permission#MANAGE_USERS} or
@@ -1420,6 +1419,7 @@
return false;
}
+ // TODO(b/239982558): pass proper displayId
return startUserNoChecks(userId, Display.DEFAULT_DISPLAY, /* foreground= */ false,
/* unlockListener= */ null);
}
@@ -1477,7 +1477,7 @@
checkCallingHasOneOfThosePermissions("startUserOnSecondaryDisplay",
MANAGE_USERS, CREATE_USERS);
- // DEFAULT_DISPLAY is used for "regular" start user operations
+ // DEFAULT_DISPLAY is used for the current foreground user only
Preconditions.checkArgument(displayId != Display.DEFAULT_DISPLAY,
"Cannot use DEFAULT_DISPLAY");
@@ -1510,27 +1510,13 @@
foreground ? " in foreground" : "");
}
- // TODO(b/239982558): move logic below to a different class (like DisplayAssignmentManager)
if (displayId != Display.DEFAULT_DISPLAY) {
- // This is called by startUserOnSecondaryDisplay()
- if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
- // TODO(b/239824814): add CTS test and/or unit test for all exceptional cases
- throw new UnsupportedOperationException("Not supported by device");
- }
-
- // TODO(b/239982558): call DisplayManagerInternal to check if display is valid instead
- Preconditions.checkArgument(displayId > 0, "Invalid display id (%d)", displayId);
- Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot start system user"
- + " on secondary display (%d)", displayId);
Preconditions.checkArgument(!foreground, "Cannot start user %d in foreground AND "
+ "on secondary display (%d)", userId, displayId);
-
- // TODO(b/239982558): for now we're just updating the user's visibility, but most likely
- // we'll need to remove this call and handle that as part of the user state workflow
- // instead.
- mInjector.getUserManagerInternal().assignUserToDisplay(userId, displayId);
}
+ mInjector.getUserManagerInternal().assignUserToDisplay(userId, displayId);
+ // TODO(b/239982558): log display id (or use a new event)
EventLog.writeEvent(EventLogTags.UC_START_USER_INTERNAL, userId);
final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 248e35e..7bdcfdb 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -267,6 +267,10 @@
OP_CAMERA,
};
+ private static final int[] WATCHABLE_NON_PERMISSION_OPS = {
+ OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
+ };
+
private static final int MAX_UNFORWARDED_OPS = 10;
private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
private static final int RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS = 300000;
@@ -574,7 +578,7 @@
mAppOpsServiceInterface.removeUid(uid);
if (pkgOps != null) {
for (String packageName : pkgOps.keySet()) {
- mAppOpsServiceInterface.removePackage(packageName);
+ mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
}
}
pkgOps = null;
@@ -584,7 +588,8 @@
boolean areAllPackageModesDefault = true;
if (pkgOps != null) {
for (String packageName : pkgOps.keySet()) {
- if (!mAppOpsServiceInterface.arePackageModesDefault(packageName)) {
+ if (!mAppOpsServiceInterface.arePackageModesDefault(packageName,
+ UserHandle.getUserId(uid))) {
areAllPackageModesDefault = false;
break;
}
@@ -664,7 +669,8 @@
if (pkgOps != null) {
for (int i = pkgOps.size() - 1; i >= 0; i--) {
foregroundOps = mAppOpsServiceInterface
- .evalForegroundPackageOps(pkgOps.valueAt(i).packageName, foregroundOps);
+ .evalForegroundPackageOps(pkgOps.valueAt(i).packageName, foregroundOps,
+ UserHandle.getUserId(uid));
}
}
hasForegroundWatchers = false;
@@ -1467,10 +1473,12 @@
}
@Mode int getMode() {
- return mAppOpsServiceInterface.getPackageMode(packageName, this.op);
+ return mAppOpsServiceInterface.getPackageMode(packageName, this.op,
+ UserHandle.getUserId(this.uid));
}
void setMode(@Mode int mode) {
- mAppOpsServiceInterface.setPackageMode(packageName, this.op, mode);
+ mAppOpsServiceInterface.setPackageMode(packageName, this.op, mode,
+ UserHandle.getUserId(this.uid));
}
int evalMode() {
@@ -1814,7 +1822,7 @@
if (uidState == null || uidState.pkgOps == null) {
return;
}
- mAppOpsServiceInterface.removePackage(pkgName);
+ mAppOpsServiceInterface.removePackage(pkgName, UserHandle.getUserId(uid));
Ops removedOps = uidState.pkgOps.remove(pkgName);
if (removedOps != null) {
scheduleFastWriteLocked();
@@ -2041,7 +2049,7 @@
// Remove any package state if such.
if (uidState.pkgOps != null) {
removedOps = uidState.pkgOps.remove(packageName);
- mAppOpsServiceInterface.removePackage(packageName);
+ mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
}
// If we just nuked the last package state check if the UID is valid.
@@ -2491,7 +2499,8 @@
ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (pkgOps != null) {
pkgOps.remove(ops.packageName);
- mAppOpsServiceInterface.removePackage(ops.packageName);
+ mAppOpsServiceInterface.removePackage(ops.packageName,
+ UserHandle.getUserId(uidState.uid));
if (pkgOps.isEmpty()) {
uidState.pkgOps = null;
}
@@ -2944,7 +2953,8 @@
}
if (pkgOps.size() == 0) {
it.remove();
- mAppOpsServiceInterface.removePackage(packageName);
+ mAppOpsServiceInterface.removePackage(packageName,
+ UserHandle.getUserId(uidState.uid));
}
}
if (uidState.isDefault()) {
@@ -4242,6 +4252,10 @@
public boolean shouldCollectNotes(int opCode) {
Preconditions.checkArgumentInRange(opCode, 0, _NUM_OP - 1, "opCode");
+ if (ArrayUtils.contains(WATCHABLE_NON_PERMISSION_OPS, opCode)) {
+ return true;
+ }
+
String perm = AppOpsManager.opToPermission(opCode);
if (perm == null) {
return false;
@@ -6646,7 +6660,7 @@
return;
}
Ops removedOps = uidState.pkgOps.remove(packageName);
- mAppOpsServiceInterface.removePackage(packageName);
+ mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
if (removedOps != null) {
scheduleFastWriteLocked();
}
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
index c707086..c4a9a4b 100644
--- a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
+++ b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppOpsManager.Mode;
import android.util.ArraySet;
import android.util.SparseBooleanArray;
@@ -61,23 +62,27 @@
* Returns default op mode if the op mode for the particular package is not set.
* @param packageName package name for which we need the op mode.
* @param op app-op for which we need the mode.
+ * @param userId user id associated with the package.
* @return the mode of the app-op.
*/
- int getPackageMode(@NonNull String packageName, int op);
+ int getPackageMode(@NonNull String packageName, int op, @UserIdInt int userId);
/**
* Sets the app-op mode for a particular package.
* @param packageName package name for which we need to set the op mode.
* @param op app-op for which we need to set the mode.
* @param mode the mode of the app-op.
+ * @param userId user id associated with the package.
+ *
*/
- void setPackageMode(@NonNull String packageName, int op, @Mode int mode);
+ void setPackageMode(@NonNull String packageName, int op, @Mode int mode, @UserIdInt int userId);
/**
* Stop tracking any app-op modes for a package.
* @param packageName Name of the package for which we want to remove all mode tracking.
+ * @param userId user id associated with the package.
*/
- boolean removePackage(@NonNull String packageName);
+ boolean removePackage(@NonNull String packageName, @UserIdInt int userId);
/**
* Stop tracking any app-op modes for this uid.
@@ -96,8 +101,9 @@
* Returns true if all package modes for this package name are
* in default state.
* @param packageName package name.
+ * @param userId user id associated with the package.
*/
- boolean arePackageModesDefault(String packageName);
+ boolean arePackageModesDefault(String packageName, @UserIdInt int userId);
/**
* Stop tracking app-op modes for all uid and packages.
@@ -177,10 +183,11 @@
* foregroundOps.
* @param packageName for which the app-op's mode needs to be marked.
* @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
+ * @param userId user id associated with the package.
* @return foregroundOps.
*/
SparseBooleanArray evalForegroundPackageOps(String packageName,
- SparseBooleanArray foregroundOps);
+ SparseBooleanArray foregroundOps, @UserIdInt int userId);
/**
* Dump op mode and package mode listeners and their details.
diff --git a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
index 2d498ea..80266972 100644
--- a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
+++ b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
@@ -25,6 +25,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManager.Mode;
@@ -70,7 +71,7 @@
final SparseArray<SparseIntArray> mUidModes = new SparseArray<>();
@GuardedBy("mLock")
- final ArrayMap<String, SparseIntArray> mPackageModes = new ArrayMap<>();
+ final SparseArray<ArrayMap<String, SparseIntArray>> mUserPackageModes = new SparseArray<>();
final SparseArray<ArraySet<OnOpModeChangedListener>> mOpModeWatchers = new SparseArray<>();
final ArrayMap<String, ArraySet<OnOpModeChangedListener>> mPackageModeWatchers =
@@ -147,9 +148,13 @@
}
@Override
- public int getPackageMode(String packageName, int op) {
+ public int getPackageMode(String packageName, int op, @UserIdInt int userId) {
synchronized (mLock) {
- SparseIntArray opModes = mPackageModes.getOrDefault(packageName, null);
+ ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
+ if (packageModes == null) {
+ return AppOpsManager.opToDefaultMode(op);
+ }
+ SparseIntArray opModes = packageModes.getOrDefault(packageName, null);
if (opModes == null) {
return AppOpsManager.opToDefaultMode(op);
}
@@ -158,14 +163,19 @@
}
@Override
- public void setPackageMode(String packageName, int op, @Mode int mode) {
+ public void setPackageMode(String packageName, int op, @Mode int mode, @UserIdInt int userId) {
final int defaultMode = AppOpsManager.opToDefaultMode(op);
synchronized (mLock) {
- SparseIntArray opModes = mPackageModes.get(packageName);
+ ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
+ if (packageModes == null) {
+ packageModes = new ArrayMap<>();
+ mUserPackageModes.put(userId, packageModes);
+ }
+ SparseIntArray opModes = packageModes.get(packageName);
if (opModes == null) {
if (mode != defaultMode) {
opModes = new SparseIntArray();
- mPackageModes.put(packageName, opModes);
+ packageModes.put(packageName, opModes);
opModes.put(op, mode);
mPersistenceScheduler.scheduleWriteLocked();
}
@@ -177,7 +187,7 @@
opModes.delete(op);
if (opModes.size() <= 0) {
opModes = null;
- mPackageModes.remove(packageName);
+ packageModes.remove(packageName);
}
} else {
opModes.put(op, mode);
@@ -208,17 +218,25 @@
}
@Override
- public boolean arePackageModesDefault(String packageMode) {
+ public boolean arePackageModesDefault(String packageMode, @UserIdInt int userId) {
synchronized (mLock) {
- SparseIntArray opModes = mPackageModes.get(packageMode);
+ ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
+ if (packageModes == null) {
+ return true;
+ }
+ SparseIntArray opModes = packageModes.get(packageMode);
return (opModes == null || opModes.size() <= 0);
}
}
@Override
- public boolean removePackage(String packageName) {
+ public boolean removePackage(String packageName, @UserIdInt int userId) {
synchronized (mLock) {
- SparseIntArray ops = mPackageModes.remove(packageName);
+ ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
+ if (packageModes == null) {
+ return false;
+ }
+ SparseIntArray ops = packageModes.remove(packageName);
if (ops != null) {
mPersistenceScheduler.scheduleFastWriteLocked();
return true;
@@ -231,7 +249,7 @@
public void clearAllModes() {
synchronized (mLock) {
mUidModes.clear();
- mPackageModes.clear();
+ mUserPackageModes.clear();
}
}
@@ -468,9 +486,11 @@
@Override
public SparseBooleanArray evalForegroundPackageOps(String packageName,
- SparseBooleanArray foregroundOps) {
+ SparseBooleanArray foregroundOps, @UserIdInt int userId) {
synchronized (mLock) {
- return evalForegroundOps(mPackageModes.get(packageName), foregroundOps);
+ ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
+ return evalForegroundOps(packageModes == null ? null : packageModes.get(packageName),
+ foregroundOps);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 785040e..ec8bf55 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1010,7 +1010,9 @@
mSfxHelper = new SoundEffectsHelper(mContext);
- mSpatializerHelper = new SpatializerHelper(this, mAudioSystem);
+ final boolean headTrackingDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_spatial_audio_head_tracking_enabled_default);
+ mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, headTrackingDefault);
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 4559c56..454894e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -64,6 +64,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
/**
@@ -119,11 +120,7 @@
private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
- private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
- // a public client is one that needs an anonymized version of the playback configurations, we
- // keep track of whether there is at least one to know when we need to create the list of
- // playback configurations that do not contain uid/pid/package name information.
- private boolean mHasPublicClients = false;
+ private final ConcurrentLinkedQueue<PlayMonitorClient> mClients = new ConcurrentLinkedQueue<>();
private final Object mPlayerLock = new Object();
@GuardedBy("mPlayerLock")
@@ -550,11 +547,9 @@
+ DateFormat.getTimeInstance().format(new Date()));
synchronized(mPlayerLock) {
pw.println("\n playback listeners:");
- synchronized(mClients) {
- for (PlayMonitorClient pmc : mClients) {
- pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)")
- + pmc.toString());
- }
+ for (PlayMonitorClient pmc : mClients) {
+ pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)")
+ + pmc.toString());
}
pw.println("\n");
// all players
@@ -635,48 +630,33 @@
* @param iplayerReleased indicates if the change was due to a player being released
*/
private void dispatchPlaybackChange(boolean iplayerReleased) {
- synchronized (mClients) {
- // typical use case, nobody is listening, don't do any work
- if (mClients.isEmpty()) {
- return;
- }
- }
if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); }
final List<AudioPlaybackConfiguration> configsSystem;
- // list of playback configurations for "public consumption". It is only computed if there
+ // list of playback configurations for "public consumption". It is computed lazy if there
// are non-system playback activity listeners.
- final List<AudioPlaybackConfiguration> configsPublic;
+ List<AudioPlaybackConfiguration> configsPublic = null;
synchronized (mPlayerLock) {
if (mPlayers.isEmpty()) {
return;
}
- configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
+ configsSystem = new ArrayList<>(mPlayers.values());
}
- synchronized (mClients) {
- // was done at beginning of method, but could have changed
- if (mClients.isEmpty()) {
- return;
- }
- configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null;
- final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
- while (clientIterator.hasNext()) {
- final PlayMonitorClient pmc = clientIterator.next();
- try {
- // do not spam the logs if there are problems communicating with this client
- if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) {
- if (pmc.mIsPrivileged) {
- pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem,
- iplayerReleased);
- } else {
- // non-system clients don't have the control interface IPlayer, so
- // they don't need to flush commands when a player was released
- pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false);
- }
+
+ final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
+ while (clientIterator.hasNext()) {
+ final PlayMonitorClient pmc = clientIterator.next();
+ // do not spam the logs if there are problems communicating with this client
+ if (!pmc.reachedMaxErrorCount()) {
+ if (pmc.isPrivileged()) {
+ pmc.dispatchPlaybackConfigChange(configsSystem,
+ iplayerReleased);
+ } else {
+ if (configsPublic == null) {
+ configsPublic = anonymizeForPublicConsumption(configsSystem);
}
- } catch (RemoteException e) {
- pmc.mErrorCount++;
- Log.e(TAG, "Error (" + pmc.mErrorCount +
- ") trying to dispatch playback config change to " + pmc, e);
+ // non-system clients don't have the control interface IPlayer, so
+ // they don't need to flush commands when a player was released
+ pmc.dispatchPlaybackConfigChange(configsPublic, false);
}
}
}
@@ -899,14 +879,9 @@
if (pcdb == null) {
return;
}
- synchronized(mClients) {
- final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
- if (pmc.init()) {
- if (!isPrivileged) {
- mHasPublicClients = true;
- }
- mClients.add(pmc);
- }
+ final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
+ if (pmc.init()) {
+ mClients.add(pmc);
}
}
@@ -914,23 +889,14 @@
if (pcdb == null) {
return;
}
- synchronized(mClients) {
- final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
- boolean hasPublicClients = false;
- // iterate over the clients to remove the dispatcher to remove, and reevaluate at
- // the same time if we still have a public client.
- while (clientIterator.hasNext()) {
- PlayMonitorClient pmc = clientIterator.next();
- if (pcdb.asBinder().equals(pmc.mDispatcherCb.asBinder())) {
- pmc.release();
- clientIterator.remove();
- } else {
- if (!pmc.mIsPrivileged) {
- hasPublicClients = true;
- }
- }
+ final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
+ // iterate over the clients to remove the dispatcher
+ while (clientIterator.hasNext()) {
+ PlayMonitorClient pmc = clientIterator.next();
+ if (pmc.equalsDispatcher(pcdb)) {
+ pmc.release();
+ clientIterator.remove();
}
- mHasPublicClients = hasPublicClients;
}
}
@@ -958,24 +924,34 @@
// can afford to be static because only one PlaybackActivityMonitor ever instantiated
static PlaybackActivityMonitor sListenerDeathMonitor;
- final IPlaybackConfigDispatcher mDispatcherCb;
- final boolean mIsPrivileged;
-
- int mErrorCount = 0;
// number of errors after which we don't update this client anymore to not spam the logs
- static final int MAX_ERRORS = 5;
+ private static final int MAX_ERRORS = 5;
+
+ private final IPlaybackConfigDispatcher mDispatcherCb;
+
+ @GuardedBy("this")
+ private final boolean mIsPrivileged;
+ @GuardedBy("this")
+ private boolean mIsReleased = false;
+ @GuardedBy("this")
+ private int mErrorCount = 0;
PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
mDispatcherCb = pcdb;
mIsPrivileged = isPrivileged;
}
+ @Override
public void binderDied() {
Log.w(TAG, "client died");
sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
}
- boolean init() {
+ synchronized boolean init() {
+ if (mIsReleased) {
+ // Do not init after release
+ return false;
+ }
try {
mDispatcherCb.asBinder().linkToDeath(this, 0);
return true;
@@ -985,8 +961,43 @@
}
}
- void release() {
+ synchronized void release() {
mDispatcherCb.asBinder().unlinkToDeath(this, 0);
+ mIsReleased = true;
+ }
+
+ void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
+ boolean flush) {
+ synchronized (this) {
+ if (mIsReleased) {
+ // Do not dispatch anything after release
+ return;
+ }
+ }
+ try {
+ mDispatcherCb.dispatchPlaybackConfigChange(configs, flush);
+ } catch (RemoteException e) {
+ synchronized (this) {
+ mErrorCount++;
+ Log.e(TAG, "Error (" + mErrorCount
+ + ") trying to dispatch playback config change to " + this, e);
+ }
+ }
+ }
+
+ synchronized boolean isPrivileged() {
+ return mIsPrivileged;
+ }
+
+ synchronized boolean reachedMaxErrorCount() {
+ return mErrorCount >= MAX_ERRORS;
+ }
+
+ synchronized boolean equalsDispatcher(IPlaybackConfigDispatcher pcdb) {
+ if (pcdb == null) {
+ return false;
+ }
+ return pcdb.asBinder().equals(mDispatcherCb.asBinder());
}
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index c143675..8e8fd05 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -169,9 +169,20 @@
//------------------------------------------------------
// initialization
- SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa) {
+ @SuppressWarnings("StaticAssignmentInConstructor")
+ SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa,
+ boolean headTrackingEnabledByDefault) {
mAudioService = mother;
mASA = asa;
+ // "StaticAssignmentInConstructor" warning is suppressed as the SpatializerHelper being
+ // constructed here is the factory for SADeviceState, thus SADeviceState and its
+ // private static field sHeadTrackingEnabledDefault should never be accessed directly.
+ SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledByDefault;
+ }
+
+ synchronized void initForTest(boolean hasBinaural, boolean hasTransaural) {
+ mBinauralSupported = hasBinaural;
+ mTransauralSupported = hasTransaural;
}
synchronized void init(boolean effectExpected, @Nullable String settings) {
@@ -1502,18 +1513,26 @@
}
/*package*/ static final class SADeviceState {
+ private static boolean sHeadTrackingEnabledDefault = false;
final @AudioDeviceInfo.AudioDeviceType int mDeviceType;
final @NonNull String mDeviceAddress;
boolean mEnabled = true; // by default, SA is enabled on any device
boolean mHasHeadTracker = false;
- boolean mHeadTrackerEnabled = true; // by default, if head tracker is present, use it
+ boolean mHeadTrackerEnabled;
static final String SETTING_FIELD_SEPARATOR = ",";
static final String SETTING_DEVICE_SEPARATOR_CHAR = "|";
static final String SETTING_DEVICE_SEPARATOR = "\\|";
- SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @NonNull String address) {
+ /**
+ * Constructor
+ * @param deviceType
+ * @param address must be non-null for wireless devices
+ * @throws NullPointerException if a null address is passed for a wireless device
+ */
+ SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @Nullable String address) {
mDeviceType = deviceType;
mDeviceAddress = isWireless(deviceType) ? Objects.requireNonNull(address) : "";
+ mHeadTrackerEnabled = sHeadTrackingEnabledDefault;
}
@Override
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 e18e31ec..2e4c323 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
@@ -100,9 +100,7 @@
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, null /* taskStackListener */, lockoutCache,
allowBackgroundAuthentication,
- context.getResources().getBoolean(
- com.android.internal.R.bool.system_server_plays_face_haptics)
- /* shouldVibrate */,
+ false /* shouldVibrate */,
isKeyguardBypassEnabled);
setRequestId(requestId);
mUsageStats = usageStats;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 9baca98..91eec7d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -74,7 +74,7 @@
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, null /* taskStackListener */,
- lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
+ lockoutTracker, allowBackgroundAuthentication, false /* shouldVibrate */,
isKeyguardBypassEnabled);
setRequestId(requestId);
mUsageStats = usageStats;
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 f7d94c9..e0955b7 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
@@ -131,7 +131,7 @@
taskStackListener,
lockoutCache,
allowBackgroundAuthentication,
- true /* shouldVibrate */,
+ false /* shouldVibrate */,
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutCache = lockoutCache;
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 7ed1a51..0d620fd 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
@@ -81,7 +81,7 @@
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
- true /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
+ false /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index c835d2f..25d0752 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -116,10 +116,8 @@
luxLevels = getLuxLevels(resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevelsIdle));
} else {
- brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
- luxLevels = getLuxLevels(resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLevels));
+ brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
+ luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux();
}
// Display independent, mode independent values
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 4f3fd64..12b2f47 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.os.Environment;
@@ -149,12 +150,22 @@
* </quirks>
*
* <autoBrightness>
- * <brighteningLightDebounceMillis>
+ * <brighteningLightDebounceMillis>
* 2000
- * </brighteningLightDebounceMillis>
+ * </brighteningLightDebounceMillis>
* <darkeningLightDebounceMillis>
* 1000
* </darkeningLightDebounceMillis>
+ * <displayBrightnessMapping>
+ * <displayBrightnessPoint>
+ * <lux>50</lux>
+ * <nits>45.32</nits>
+ * </displayBrightnessPoint>
+ * <displayBrightnessPoint>
+ * <lux>80</lux>
+ * <nits>75.43</nits>
+ * </displayBrightnessPoint>
+ * </displayBrightnessMapping>
* </autoBrightness>
*
* <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
@@ -268,6 +279,34 @@
// for the corresponding values above
private float[] mBrightness;
+
+ /**
+ * Array of desired screen brightness in nits corresponding to the lux values
+ * in the mBrightnessLevelsLux array. The display brightness is defined as the
+ * measured brightness of an all-white image. The brightness values must be non-negative and
+ * non-decreasing. This must be overridden in platform specific overlays
+ */
+ private float[] mBrightnessLevelsNits;
+
+ /**
+ * Array of light sensor lux values to define our levels for auto backlight
+ * brightness support.
+
+ * The N + 1 entries of this array define N control points defined in mBrightnessLevelsNits,
+ * with first value always being 0 lux
+
+ * The control points must be strictly increasing. Each control point
+ * corresponds to an entry in the brightness backlight values arrays.
+ * For example, if lux == level[1] (second element of the levels array)
+ * then the brightness will be determined by value[0] (first element
+ * of the brightness values array).
+ *
+ * Spline interpolation is used to determine the auto-brightness
+ * backlight values for lux levels between these control points.
+ *
+ */
+ private float[] mBrightnessLevelsLux;
+
private float mBacklightMinimum = Float.NaN;
private float mBacklightMaximum = Float.NaN;
private float mBrightnessDefault = Float.NaN;
@@ -661,6 +700,20 @@
return mAutoBrightnessBrighteningLightDebounce;
}
+ /**
+ * @return Auto brightness brightening ambient lux levels
+ */
+ public float[] getAutoBrightnessBrighteningLevelsLux() {
+ return mBrightnessLevelsLux;
+ }
+
+ /**
+ * @return Auto brightness brightening nits levels
+ */
+ public float[] getAutoBrightnessBrighteningLevelsNits() {
+ return mBrightnessLevelsNits;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -703,6 +756,8 @@
+ mAutoBrightnessBrighteningLightDebounce
+ ", mAutoBrightnessDarkeningLightDebounce= "
+ mAutoBrightnessDarkeningLightDebounce
+ + ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
+ + ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ "}";
}
@@ -779,6 +834,7 @@
loadBrightnessRampsFromConfigXml();
loadAmbientLightSensorFromConfigXml();
setProxSensorUnspecified();
+ loadAutoBrightnessConfigsFromConfigXml();
mLoadedFrom = "<config.xml>";
}
@@ -991,6 +1047,7 @@
private void loadAutoBrightnessConfigValues(DisplayConfiguration config) {
loadAutoBrightnessBrighteningLightDebounce(config.getAutoBrightness());
loadAutoBrightnessDarkeningLightDebounce(config.getAutoBrightness());
+ loadAutoBrightnessDisplayBrightnessMapping(config.getAutoBrightness());
}
/**
@@ -1023,6 +1080,35 @@
}
}
+ /**
+ * Loads the auto-brightness display brightness mappings. Internally, this takes care of
+ * loading the value from the display config, and if not present, falls back to config.xml.
+ */
+ private void loadAutoBrightnessDisplayBrightnessMapping(AutoBrightness autoBrightnessConfig) {
+ if (autoBrightnessConfig == null
+ || autoBrightnessConfig.getDisplayBrightnessMapping() == null) {
+ mBrightnessLevelsNits = getFloatArray(mContext.getResources()
+ .obtainTypedArray(com.android.internal.R.array
+ .config_autoBrightnessDisplayValuesNits), PowerManager
+ .BRIGHTNESS_OFF_FLOAT);
+ mBrightnessLevelsLux = getLuxLevels(mContext.getResources()
+ .getIntArray(com.android.internal.R.array
+ .config_autoBrightnessLevels));
+ } else {
+ final int size = autoBrightnessConfig.getDisplayBrightnessMapping()
+ .getDisplayBrightnessPoint().size();
+ mBrightnessLevelsNits = new float[size];
+ // The first control point is implicit and always at 0 lux.
+ mBrightnessLevelsLux = new float[size + 1];
+ for (int i = 0; i < size; i++) {
+ mBrightnessLevelsNits[i] = autoBrightnessConfig.getDisplayBrightnessMapping()
+ .getDisplayBrightnessPoint().get(i).getNits().floatValue();
+ mBrightnessLevelsLux[i + 1] = autoBrightnessConfig.getDisplayBrightnessMapping()
+ .getDisplayBrightnessPoint().get(i).getLux().floatValue();
+ }
+ }
+ }
+
private void loadBrightnessMapFromConfigXml() {
// Use the config.xml mapping
final Resources res = mContext.getResources();
@@ -1248,6 +1334,10 @@
com.android.internal.R.string.config_displayLightSensorType);
}
+ private void loadAutoBrightnessConfigsFromConfigXml() {
+ loadAutoBrightnessDisplayBrightnessMapping(null /*AutoBrightnessConfig*/);
+ }
+
private void loadAmbientLightSensorFromDdc(DisplayConfiguration config) {
final SensorDetails sensorDetails = config.getLightSensor();
if (sensorDetails != null) {
@@ -1390,6 +1480,31 @@
}
}
+ /**
+ * Extracts a float array from the specified {@link TypedArray}.
+ *
+ * @param array The array to convert.
+ * @return the given array as a float array.
+ */
+ public static float[] getFloatArray(TypedArray array, float defaultValue) {
+ final int n = array.length();
+ float[] vals = new float[n];
+ for (int i = 0; i < n; i++) {
+ vals[i] = array.getFloat(i, defaultValue);
+ }
+ array.recycle();
+ return vals;
+ }
+
+ private static float[] getLuxLevels(int[] lux) {
+ // The first control point is implicit and always at 0 lux.
+ float[] levels = new float[lux.length + 1];
+ for (int i = 0; i < lux.length; i++) {
+ levels[i + 1] = (float) lux[i];
+ }
+ return levels;
+ }
+
static class SensorData {
public String type;
public String name;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 90a208e..58a182a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -413,11 +413,8 @@
// For a new display, we need to initialize the default mode ID.
if (mDefaultModeId == INVALID_MODE_ID) {
- mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID
- ? mSystemPreferredModeId : activeRecord.mMode.getModeId();
- mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID
- ? preferredSfDisplayMode.group
- : mActiveSfDisplayMode.group;
+ mDefaultModeId = activeRecord.mMode.getModeId();
+ mDefaultModeGroup = mActiveSfDisplayMode.group;
} else if (modesAdded && activeModeChanged) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
@@ -911,6 +908,13 @@
public void setUserPreferredDisplayModeLocked(Display.Mode mode) {
final int oldModeId = getPreferredModeId();
mUserPreferredMode = mode;
+ // When clearing the user preferred mode we need to also reset the default mode. This is
+ // used by DisplayModeDirector to determine the default resolution, so if we don't clear
+ // it then the resolution won't reset to what it would've been prior to setting a user
+ // preferred display mode.
+ if (mode == null && mSystemPreferredModeId != INVALID_MODE_ID) {
+ mDefaultModeId = mSystemPreferredModeId;
+ }
if (mode != null && (mode.isRefreshRateSet() || mode.isResolutionSet())) {
Display.Mode matchingSupportedMode;
matchingSupportedMode = findMode(mode.getPhysicalWidth(),
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 8c03ead..1d1057f 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -676,6 +676,11 @@
return mInjector.getSettingsHelper().getIgnoreSettingsAllowlist();
}
+ @Override
+ public PackageTagsList getAdasAllowlist() {
+ return mInjector.getSettingsHelper().getAdasAllowlist();
+ }
+
@Nullable
@Override
public ICancellationSignal getCurrentLocation(String provider, LocationRequest request,
diff --git a/services/core/java/com/android/server/location/contexthub/ConcurrentLinkedEvictingDeque.java b/services/core/java/com/android/server/location/contexthub/ConcurrentLinkedEvictingDeque.java
index 0427007..5ed0d0d 100644
--- a/services/core/java/com/android/server/location/contexthub/ConcurrentLinkedEvictingDeque.java
+++ b/services/core/java/com/android/server/location/contexthub/ConcurrentLinkedEvictingDeque.java
@@ -33,7 +33,7 @@
@Override
public boolean add(E elem) {
synchronized (this) {
- if (size() == mSize) {
+ if (size() == mSize) { // TODO(b/244459069): the size() method is not constant time
poll();
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index ffdb66d..5819ff0 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -56,6 +56,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.location.ClientBrokerProto;
+import java.util.Base64;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -470,8 +471,16 @@
+ mAttachedContextHubInfo.getId() + ")", e);
result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
+
+ ContextHubEventLogger.getInstance().logMessageToNanoapp(
+ mAttachedContextHubInfo.getId(),
+ message,
+ result == ContextHubTransaction.RESULT_SUCCESS);
} else {
- Log.e(TAG, "Failed to send message to nanoapp: client connection is closed");
+ String messageString = Base64.getEncoder().encodeToString(message.getMessageBody());
+ Log.e(TAG, String.format(
+ "Failed to send message (connection closed): hostEndpointId= %1$d payload %2$s",
+ mHostEndPointId, messageString));
result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
index 41a406ce..4de7c0c 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
@@ -31,9 +31,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -48,11 +45,6 @@
private static final String TAG = "ContextHubClientManager";
/*
- * The DateFormat for printing RegistrationRecord.
- */
- private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd HH:mm:ss.SSS");
-
- /*
* The maximum host endpoint ID value that a client can be assigned.
*/
private static final int MAX_CLIENT_ID = 0x7fff;
@@ -125,14 +117,15 @@
@Override
public String toString() {
- String out = "";
- out += DATE_FORMAT.format(new Date(mTimestamp)) + " ";
- out += mAction == ACTION_REGISTERED ? "+ " : "- ";
- out += mBroker;
+ StringBuilder sb = new StringBuilder();
+ sb.append(ContextHubServiceUtil.formatDateFromTimestamp(mTimestamp));
+ sb.append(" ");
+ sb.append(mAction == ACTION_REGISTERED ? "+ " : "- ");
+ sb.append(mBroker);
if (mAction == ACTION_CANCELLED) {
- out += " (cancelled)";
+ sb.append(" (cancelled)");
}
- return out;
+ return sb.toString();
}
}
@@ -247,14 +240,17 @@
+ message.getNanoAppId());
}
- broadcastMessage(
- contextHubId, message, nanoappPermissions, messagePermissions);
+ ContextHubEventLogger.getInstance().logMessageFromNanoapp(contextHubId, message, true);
+ broadcastMessage(contextHubId, message, nanoappPermissions, messagePermissions);
} else {
ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(hostEndpointId);
if (proxy != null) {
- proxy.sendMessageToClient(
- message, nanoappPermissions, messagePermissions);
+ ContextHubEventLogger.getInstance().logMessageFromNanoapp(contextHubId, message,
+ true);
+ proxy.sendMessageToClient(message, nanoappPermissions, messagePermissions);
} else {
+ ContextHubEventLogger.getInstance().logMessageFromNanoapp(contextHubId, message,
+ false);
Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = "
+ hostEndpointId + ")");
}
@@ -414,17 +410,21 @@
@Override
public String toString() {
- String out = "";
+ StringBuilder sb = new StringBuilder();
for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
- out += broker + "\n";
+ sb.append(broker);
+ sb.append(System.lineSeparator());
}
- out += "\nRegistration history:\n";
+ sb.append(System.lineSeparator());
+ sb.append("Registration History:");
+ sb.append(System.lineSeparator());
Iterator<RegistrationRecord> it = mRegistrationRecordDeque.descendingIterator();
while (it.hasNext()) {
- out += it.next() + "\n";
+ sb.append(it.next());
+ sb.append(System.lineSeparator());
}
- return out;
+ return sb.toString();
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
index e46bb74..15b2093 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
@@ -17,6 +17,9 @@
package com.android.server.location.contexthub;
import android.hardware.location.NanoAppMessage;
+import android.util.Log;
+
+import java.util.Collection;
/**
* A class to log events and useful metrics within the Context Hub service.
@@ -30,35 +33,238 @@
* @hide
*/
public class ContextHubEventLogger {
+
+ /**
+ * The base class for all Context Hub events
+ */
+ public static class ContextHubEventBase {
+ /**
+ * the timestamp in milliseconds
+ */
+ public final long timeStampInMs;
+
+ /**
+ * the ID of the context hub
+ */
+ public final int contextHubId;
+
+ public ContextHubEventBase(long mTimeStampInMs, int mContextHubId) {
+ timeStampInMs = mTimeStampInMs;
+ contextHubId = mContextHubId;
+ }
+ }
+
+ /**
+ * A base class for nanoapp events
+ */
+ public static class NanoappEventBase extends ContextHubEventBase {
+ /**
+ * the ID of the nanoapp
+ */
+ public final long nanoappId;
+
+ /**
+ * whether the event was successful
+ */
+ public final boolean success;
+
+ public NanoappEventBase(long mTimeStampInMs, int mContextHubId,
+ long mNanoappId, boolean mSuccess) {
+ super(mTimeStampInMs, mContextHubId);
+ nanoappId = mNanoappId;
+ success = mSuccess;
+ }
+ }
+
+ /**
+ * Represents a nanoapp load event
+ */
+ public static class NanoappLoadEvent extends NanoappEventBase {
+ /**
+ * the version of the nanoapp
+ */
+ public final int nanoappVersion;
+
+ /**
+ * the size in bytes of the nanoapp
+ */
+ public final long nanoappSize;
+
+ public NanoappLoadEvent(long mTimeStampInMs, int mContextHubId, long mNanoappId,
+ int mNanoappVersion, long mNanoappSize, boolean mSuccess) {
+ super(mTimeStampInMs, mContextHubId, mNanoappId, mSuccess);
+ nanoappVersion = mNanoappVersion;
+ nanoappSize = mNanoappSize;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(ContextHubServiceUtil.formatDateFromTimestamp(timeStampInMs));
+ sb.append(": NanoappLoadEvent[hubId = ");
+ sb.append(contextHubId);
+ sb.append(", appId = 0x");
+ sb.append(Long.toHexString(nanoappId));
+ sb.append(", appVersion = ");
+ sb.append(nanoappVersion);
+ sb.append(", appSize = ");
+ sb.append(nanoappSize);
+ sb.append(" bytes, success = ");
+ sb.append(success ? "true" : "false");
+ sb.append(']');
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Represents a nanoapp unload event
+ */
+ public static class NanoappUnloadEvent extends NanoappEventBase {
+ public NanoappUnloadEvent(long mTimeStampInMs, int mContextHubId,
+ long mNanoappId, boolean mSuccess) {
+ super(mTimeStampInMs, mContextHubId, mNanoappId, mSuccess);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(ContextHubServiceUtil.formatDateFromTimestamp(timeStampInMs));
+ sb.append(": NanoappUnloadEvent[hubId = ");
+ sb.append(contextHubId);
+ sb.append(", appId = 0x");
+ sb.append(Long.toHexString(nanoappId));
+ sb.append(", success = ");
+ sb.append(success ? "true" : "false");
+ sb.append(']');
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Represents a nanoapp message event
+ */
+ public static class NanoappMessageEvent extends NanoappEventBase {
+ /**
+ * the message that was sent
+ */
+ public final NanoAppMessage message;
+
+ public NanoappMessageEvent(long mTimeStampInMs, int mContextHubId,
+ NanoAppMessage mMessage, boolean mSuccess) {
+ super(mTimeStampInMs, mContextHubId, 0, mSuccess);
+ message = mMessage;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(ContextHubServiceUtil.formatDateFromTimestamp(timeStampInMs));
+ sb.append(": NanoappMessageEvent[hubId = ");
+ sb.append(contextHubId);
+ sb.append(", ");
+ sb.append(message.toString());
+ sb.append(", success = ");
+ sb.append(success ? "true" : "false");
+ sb.append(']');
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Represents a context hub restart event
+ */
+ public static class ContextHubRestartEvent extends ContextHubEventBase {
+ public ContextHubRestartEvent(long mTimeStampInMs, int mContextHubId) {
+ super(mTimeStampInMs, mContextHubId);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(ContextHubServiceUtil.formatDateFromTimestamp(timeStampInMs));
+ sb.append(": ContextHubRestartEvent[hubId = ");
+ sb.append(contextHubId);
+ sb.append(']');
+ return sb.toString();
+ }
+ }
+
+ public static final int NUM_EVENTS_TO_STORE = 20;
private static final String TAG = "ContextHubEventLogger";
- ContextHubEventLogger() {
- throw new RuntimeException("Not implemented");
+ private final ConcurrentLinkedEvictingDeque<NanoappLoadEvent> mNanoappLoadEventQueue =
+ new ConcurrentLinkedEvictingDeque<>(NUM_EVENTS_TO_STORE);
+ private final ConcurrentLinkedEvictingDeque<NanoappUnloadEvent> mNanoappUnloadEventQueue =
+ new ConcurrentLinkedEvictingDeque<>(NUM_EVENTS_TO_STORE);
+ private final ConcurrentLinkedEvictingDeque<NanoappMessageEvent> mMessageFromNanoappQueue =
+ new ConcurrentLinkedEvictingDeque<>(NUM_EVENTS_TO_STORE);
+ private final ConcurrentLinkedEvictingDeque<NanoappMessageEvent> mMessageToNanoappQueue =
+ new ConcurrentLinkedEvictingDeque<>(NUM_EVENTS_TO_STORE);
+ private final ConcurrentLinkedEvictingDeque<ContextHubRestartEvent>
+ mContextHubRestartEventQueue = new ConcurrentLinkedEvictingDeque<>(NUM_EVENTS_TO_STORE);
+
+ // Make ContextHubEventLogger a singleton
+ private static ContextHubEventLogger sInstance = null;
+
+ private ContextHubEventLogger() {}
+
+ /**
+ * Gets the singleton instance for ContextHubEventLogger
+ */
+ public static synchronized ContextHubEventLogger getInstance() {
+ if (sInstance == null) {
+ sInstance = new ContextHubEventLogger();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Clears all queues of events.
+ */
+ public synchronized void clear() {
+ for (Collection<?> deque:
+ new Collection<?>[] {mNanoappLoadEventQueue, mNanoappUnloadEventQueue,
+ mMessageFromNanoappQueue, mMessageToNanoappQueue,
+ mContextHubRestartEventQueue}) {
+ deque.clear();
+ }
}
/**
* Logs a nanoapp load event
*
* @param contextHubId the ID of the context hub
- * @param nanoAppId the ID of the nanoapp
- * @param nanoAppVersion the version of the nanoapp
- * @param nanoAppSize the size in bytes of the nanoapp
+ * @param nanoappId the ID of the nanoapp
+ * @param nanoappVersion the version of the nanoapp
+ * @param nanoappSize the size in bytes of the nanoapp
* @param success whether the load was successful
*/
- public void logNanoAppLoad(int contextHubId, long nanoAppId, int nanoAppVersion,
- long nanoAppSize, boolean success) {
- throw new RuntimeException("Not implemented");
+ public synchronized void logNanoappLoad(int contextHubId, long nanoappId, int nanoappVersion,
+ long nanoappSize, boolean success) {
+ long timeStampInMs = System.currentTimeMillis();
+ NanoappLoadEvent event = new NanoappLoadEvent(timeStampInMs, contextHubId, nanoappId,
+ nanoappVersion, nanoappSize, success);
+ boolean status = mNanoappLoadEventQueue.add(event);
+ if (!status) {
+ Log.e(TAG, "Unable to add nanoapp load event to queue: " + event);
+ }
}
/**
* Logs a nanoapp unload event
*
* @param contextHubId the ID of the context hub
- * @param nanoAppId the ID of the nanoapp
+ * @param nanoappId the ID of the nanoapp
* @param success whether the unload was successful
*/
- public void logNanoAppUnload(int contextHubId, long nanoAppId, boolean success) {
- throw new RuntimeException("Not implemented");
+ public synchronized void logNanoappUnload(int contextHubId, long nanoappId, boolean success) {
+ long timeStampInMs = System.currentTimeMillis();
+ NanoappUnloadEvent event = new NanoappUnloadEvent(timeStampInMs, contextHubId,
+ nanoappId, success);
+ boolean status = mNanoappUnloadEventQueue.add(event);
+ if (!status) {
+ Log.e(TAG, "Unable to add nanoapp unload event to queue: " + event);
+ }
}
/**
@@ -66,9 +272,21 @@
*
* @param contextHubId the ID of the context hub
* @param message the message that was sent
+ * @param success whether the message was sent successfully
*/
- public void logMessageFromNanoApp(int contextHubId, NanoAppMessage message) {
- throw new RuntimeException("Not implemented");
+ public synchronized void logMessageFromNanoapp(int contextHubId, NanoAppMessage message,
+ boolean success) {
+ if (message == null) {
+ return;
+ }
+
+ long timeStampInMs = System.currentTimeMillis();
+ NanoappMessageEvent event = new NanoappMessageEvent(timeStampInMs, contextHubId,
+ message, success);
+ boolean status = mMessageFromNanoappQueue.add(event);
+ if (!status) {
+ Log.e(TAG, "Unable to add message from nanoapp event to queue: " + event);
+ }
}
/**
@@ -78,8 +296,19 @@
* @param message the message that was sent
* @param success whether the message was sent successfully
*/
- public void logMessageToNanoApp(int contextHubId, NanoAppMessage message, boolean success) {
- throw new RuntimeException("Not implemented");
+ public synchronized void logMessageToNanoapp(int contextHubId, NanoAppMessage message,
+ boolean success) {
+ if (message == null) {
+ return;
+ }
+
+ long timeStampInMs = System.currentTimeMillis();
+ NanoappMessageEvent event = new NanoappMessageEvent(timeStampInMs, contextHubId,
+ message, success);
+ boolean status = mMessageToNanoappQueue.add(event);
+ if (!status) {
+ Log.e(TAG, "Unable to add message to nanoapp event to queue: " + event);
+ }
}
/**
@@ -87,8 +316,13 @@
*
* @param contextHubId the ID of the context hub
*/
- public void logContextHubRestarts(int contextHubId) {
- throw new RuntimeException("Not implemented");
+ public synchronized void logContextHubRestart(int contextHubId) {
+ long timeStampInMs = System.currentTimeMillis();
+ ContextHubRestartEvent event = new ContextHubRestartEvent(timeStampInMs, contextHubId);
+ boolean status = mContextHubRestartEventQueue.add(event);
+ if (!status) {
+ Log.e(TAG, "Unable to add Context Hub restart event to queue: " + event);
+ }
}
/**
@@ -96,8 +330,43 @@
*
* @return the dumped events
*/
- public String dump() {
- throw new RuntimeException("Not implemented");
+ public synchronized String dump() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Nanoapp Loads:");
+ sb.append(System.lineSeparator());
+ for (NanoappLoadEvent event : mNanoappLoadEventQueue) {
+ sb.append(event);
+ sb.append(System.lineSeparator());
+ }
+ sb.append(System.lineSeparator());
+ sb.append("Nanoapp Unloads:");
+ sb.append(System.lineSeparator());
+ for (NanoappUnloadEvent event : mNanoappUnloadEventQueue) {
+ sb.append(event);
+ sb.append(System.lineSeparator());
+ }
+ sb.append(System.lineSeparator());
+ sb.append("Messages from Nanoapps:");
+ sb.append(System.lineSeparator());
+ for (NanoappMessageEvent event : mMessageFromNanoappQueue) {
+ sb.append(event);
+ sb.append(System.lineSeparator());
+ }
+ sb.append(System.lineSeparator());
+ sb.append("Messages to Nanoapps:");
+ sb.append(System.lineSeparator());
+ for (NanoappMessageEvent event : mMessageToNanoappQueue) {
+ sb.append(event);
+ sb.append(System.lineSeparator());
+ }
+ sb.append(System.lineSeparator());
+ sb.append("Context Hub Restarts:");
+ sb.append(System.lineSeparator());
+ for (ContextHubRestartEvent event : mContextHubRestartEventQueue) {
+ sb.append(event);
+ sb.append(System.lineSeparator());
+ }
+ return sb.toString();
}
@Override
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 3ad5fc1..4ca0ea6 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -71,7 +71,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -172,10 +171,6 @@
private final Map<Integer, AtomicLong> mLastRestartTimestampMap = new HashMap<>();
- private static final int MAX_NUM_OF_NANOAPP_MESSAGE_RECORDS = 10;
- private final ConcurrentLinkedEvictingDeque<NanoAppMessage> mNanoAppMessageRecords =
- new ConcurrentLinkedEvictingDeque<>(MAX_NUM_OF_NANOAPP_MESSAGE_RECORDS);
-
/**
* Class extending the callback to register with a Context Hub.
*/
@@ -728,7 +723,6 @@
NanoAppMessage message,
List<String> nanoappPermissions,
List<String> messagePermissions) {
- mNanoAppMessageRecords.add(message);
mClientManager.onMessageFromNanoApp(
contextHubId, hostEndpointId, message, nanoappPermissions, messagePermissions);
}
@@ -791,6 +785,8 @@
TimeUnit.NANOSECONDS.toMillis(now - lastRestartTimeNs),
contextHubId);
+ ContextHubEventLogger.getInstance().logContextHubRestart(contextHubId);
+
sendLocationSettingUpdate();
sendWifiSettingUpdate(true /* forceUpdate */);
sendAirplaneModeSettingUpdate();
@@ -1049,13 +1045,6 @@
mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> pw.println(info));
pw.println("");
- pw.println("=================== NANOAPPS MESSAGES ====================");
- Iterator<NanoAppMessage> iterator = mNanoAppMessageRecords.descendingIterator();
- while (iterator.hasNext()) {
- pw.println(iterator.next());
- }
-
- pw.println("");
pw.println("=================== CLIENTS ====================");
pw.println(mClientManager);
@@ -1063,6 +1052,10 @@
pw.println("=================== TRANSACTIONS ====================");
pw.println(mTransactionManager);
+ pw.println("");
+ pw.println("=================== EVENTS ====================");
+ pw.println(ContextHubEventLogger.getInstance().dump());
+
// dump eventLog
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index e9bf90f..f637149 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -31,6 +31,9 @@
import android.hardware.location.NanoAppState;
import android.util.Log;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -49,6 +52,18 @@
*/
private static final char HOST_ENDPOINT_BROADCAST = 0xFFFF;
+
+ /*
+ * The format for printing to logs.
+ */
+ private static final String DATE_FORMAT = "MM/dd HH:mm:ss.SSS";
+
+ /**
+ * The DateTimeFormatter for printing to logs.
+ */
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT)
+ .withZone(ZoneId.systemDefault());
+
/**
* Creates a ConcurrentHashMap of the Context Hub ID to the ContextHubInfo object given an
* ArrayList of HIDL ContextHub objects.
@@ -386,4 +401,15 @@
return ContextHubService.CONTEXT_HUB_EVENT_UNKNOWN;
}
}
+
+ /**
+ * Converts a timestamp in milliseconds to a properly-formatted date string for log output.
+ *
+ * @param timeStampInMs the timestamp in milliseconds
+ * @return the formatted date string
+ */
+ /* package */
+ static String formatDateFromTimestamp(long timeStampInMs) {
+ return DATE_FORMATTER.format(Instant.ofEpochMilli(timeStampInMs));
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index c199bb3..4f6d0d4 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -23,11 +23,8 @@
import android.os.RemoteException;
import android.util.Log;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.Collections;
-import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
@@ -54,11 +51,6 @@
private static final int MAX_PENDING_REQUESTS = 10000;
/*
- * The DateFormat for printing TransactionRecord.
- */
- private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd HH:mm:ss.SSS");
-
- /*
* The proxy to talk to the Context Hub
*/
private final IContextHubWrapper mContextHubProxy;
@@ -112,7 +104,7 @@
@Override
public String toString() {
- return DATE_FORMAT.format(new Date(mTimestamp)) + " " + mTransaction;
+ return ContextHubServiceUtil.formatDateFromTimestamp(mTimestamp) + " " + mTransaction;
}
}
@@ -160,6 +152,13 @@
.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_TYPE__TYPE_LOAD,
toStatsTransactionResult(result));
+ ContextHubEventLogger.getInstance().logNanoappLoad(
+ contextHubId,
+ nanoAppBinary.getNanoAppId(),
+ nanoAppBinary.getNanoAppVersion(),
+ nanoAppBinary.getBinary().length,
+ result == ContextHubTransaction.RESULT_SUCCESS);
+
if (result == ContextHubTransaction.RESULT_SUCCESS) {
// NOTE: The legacy JNI code used to do a query right after a load success
// to synchronize the service cache. Instead store the binary that was
@@ -215,6 +214,11 @@
.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_TYPE__TYPE_UNLOAD,
toStatsTransactionResult(result));
+ ContextHubEventLogger.getInstance().logNanoappUnload(
+ contextHubId,
+ nanoAppId,
+ result == ContextHubTransaction.RESULT_SUCCESS);
+
if (result == ContextHubTransaction.RESULT_SUCCESS) {
mNanoAppStateManager.removeNanoAppInstance(contextHubId, nanoAppId);
}
diff --git a/services/core/java/com/android/server/logcat/OWNERS b/services/core/java/com/android/server/logcat/OWNERS
index 87d30f3..4ce93aa 100644
--- a/services/core/java/com/android/server/logcat/OWNERS
+++ b/services/core/java/com/android/server/logcat/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1218649
+
cbrubaker@google.com
eunjeongshin@google.com
georgechan@google.com
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 34cd6a0..28c7a39 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -50,6 +50,8 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.EventLog;
import android.util.Log;
import android.view.KeyEvent;
@@ -932,6 +934,14 @@
public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
final long token = Binder.clearCallingIdentity();
try {
+ //mPackageName has been verified in MediaSessionService.enforcePackageName().
+ if (receiver != null && !TextUtils.equals(
+ mPackageName, receiver.getPackageName())) {
+ EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet logging.
+ throw new IllegalArgumentException("receiver does not belong to "
+ + "package name provided to MediaSessionRecord. Pkg = " + mPackageName
+ + ", Receiver Pkg = " + receiver.getPackageName());
+ }
if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
!= 0) {
return;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9f525d5..0b4f736 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4952,7 +4952,16 @@
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
+ // If the caller is system, take the package name from the rule's owner rather than
+ // from the caller's package.
+ String rulePkg = pkg;
+ if (isCallingUidSystem()) {
+ if (automaticZenRule.getOwner() != null) {
+ rulePkg = automaticZenRule.getOwner().getPackageName();
+ }
+ }
+
+ return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
"addAutomaticZenRule");
}
@@ -6301,21 +6310,40 @@
checkCallerIsSystem();
mHandler.post(() -> {
synchronized (mNotificationLock) {
- // strip flag from all enqueued notifications. listeners will be informed
- // in post runnable.
- List<NotificationRecord> enqueued = findNotificationsByListLocked(
- mEnqueuedNotifications, pkg, null, notificationId, userId);
- for (int i = 0; i < enqueued.size(); i++) {
- removeForegroundServiceFlagLocked(enqueued.get(i));
+ int count = getNotificationCount(pkg, userId);
+ boolean removeFgsNotification = false;
+ if (count > MAX_PACKAGE_NOTIFICATIONS) {
+ mUsageStats.registerOverCountQuota(pkg);
+ removeFgsNotification = true;
}
+ if (removeFgsNotification) {
+ NotificationRecord r = findNotificationLocked(pkg, null, notificationId,
+ userId);
+ if (r != null) {
+ if (DBG) {
+ Slog.d(TAG, "Remove FGS flag not allow. Cancel FGS notification");
+ }
+ removeFromNotificationListsLocked(r);
+ cancelNotificationLocked(r, false, REASON_APP_CANCEL, true,
+ null, SystemClock.elapsedRealtime());
+ }
+ } else {
+ // strip flag from all enqueued notifications. listeners will be informed
+ // in post runnable.
+ List<NotificationRecord> enqueued = findNotificationsByListLocked(
+ mEnqueuedNotifications, pkg, null, notificationId, userId);
+ for (int i = 0; i < enqueued.size(); i++) {
+ removeForegroundServiceFlagLocked(enqueued.get(i));
+ }
- // if posted notification exists, strip its flag and tell listeners
- NotificationRecord r = findNotificationByListLocked(
- mNotificationList, pkg, null, notificationId, userId);
- if (r != null) {
- removeForegroundServiceFlagLocked(r);
- mRankingHelper.sort(mNotificationList);
- mListeners.notifyPostedLocked(r, r);
+ // if posted notification exists, strip its flag and tell listeners
+ NotificationRecord r = findNotificationByListLocked(
+ mNotificationList, pkg, null, notificationId, userId);
+ if (r != null) {
+ removeForegroundServiceFlagLocked(r);
+ mRankingHelper.sort(mNotificationList);
+ mListeners.notifyPostedLocked(r, r);
+ }
}
}
});
@@ -7012,6 +7040,29 @@
return mPermissionHelper.hasPermission(uid);
}
+ private int getNotificationCount(String pkg, int userId) {
+ int count = 0;
+ synchronized (mNotificationLock) {
+ final int numListSize = mNotificationList.size();
+ for (int i = 0; i < numListSize; i++) {
+ final NotificationRecord existing = mNotificationList.get(i);
+ if (existing.getSbn().getPackageName().equals(pkg)
+ && existing.getSbn().getUserId() == userId) {
+ count++;
+ }
+ }
+ final int numEnqSize = mEnqueuedNotifications.size();
+ for (int i = 0; i < numEnqSize; i++) {
+ final NotificationRecord existing = mEnqueuedNotifications.get(i);
+ if (existing.getSbn().getPackageName().equals(pkg)
+ && existing.getSbn().getUserId() == userId) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
protected int getNotificationCount(String pkg, int userId, int excludedId,
String excludedTag) {
int count = 0;
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 477b8da..d8aa469 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -106,7 +106,7 @@
private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
@VisibleForTesting
- static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000;
+ static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
@VisibleForTesting
static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 50000;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5ad39f2..752ebf3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5801,6 +5801,11 @@
final Computer snapshot = snapshotComputer();
enforceOwnerRights(snapshot, packageName, Binder.getCallingUid());
mimeTypes = CollectionUtils.emptyIfNull(mimeTypes);
+ for (String mimeType : mimeTypes) {
+ if (mimeType.length() > 255) {
+ throw new IllegalArgumentException("MIME type length exceeds 255 characters");
+ }
+ }
final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
Set<String> existingMimeTypes = packageState.getMimeGroups().get(mimeGroup);
if (existingMimeTypes == null) {
@@ -5811,6 +5816,10 @@
&& existingMimeTypes.containsAll(mimeTypes)) {
return;
}
+ if (mimeTypes.size() > 500) {
+ throw new IllegalStateException("Max limit on MIME types for MIME group "
+ + mimeGroup + " exceeded for package " + packageName);
+ }
ArraySet<String> mimeTypesSet = new ArraySet<>(mimeTypes);
commitPackageStateMutation(null, packageName, packageStateWrite -> {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index b7ab381..f466655 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -41,6 +41,7 @@
import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
@@ -1125,13 +1126,18 @@
throw new IllegalArgumentException("Unsupported component type");
}
- if (comp.getIntents().isEmpty()) {
+ if (comp == null || comp.getIntents().isEmpty()) {
continue;
}
- final boolean match = comp.getIntents().stream().anyMatch(
- f -> IntentResolver.intentMatchesFilter(f.getIntentFilter(), intent,
- resolvedType));
+ boolean match = false;
+ for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
+ IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
+ if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) {
+ match = true;
+ break;
+ }
+ }
if (!match) {
Slog.w(TAG, "Intent does not match component's intent filter: " + intent);
Slog.w(TAG, "Access blocked: " + comp.getComponentName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index da89b9e..7faebb5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3044,7 +3044,12 @@
translateUserId(userId, UserHandle.USER_NULL, "runSetUserRestriction");
final IUserManager um = IUserManager.Stub.asInterface(
ServiceManager.getService(Context.USER_SERVICE));
- um.setUserRestriction(restriction, value, translatedUserId);
+ try {
+ um.setUserRestriction(restriction, value, translatedUserId);
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(e.getMessage());
+ return 1;
+ }
return 0;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index f2bcf5e..0b20683 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -280,6 +280,7 @@
private final Object mLock = new Object();
private final Object mNonPersistentUsersLock = new Object();
+ private final Object mWtfLock = new Object();
private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
@@ -444,10 +445,10 @@
@interface ShortcutOperation {
}
- @GuardedBy("mLock")
+ @GuardedBy("mWtfLock")
private int mWtfCount = 0;
- @GuardedBy("mLock")
+ @GuardedBy("mWtfLock")
private Exception mLastWtfStacktrace;
@GuardedBy("mLock")
@@ -4727,13 +4728,15 @@
mStatLogger.dump(pw, " ");
- pw.println();
- pw.print(" #Failures: ");
- pw.println(mWtfCount);
+ synchronized (mWtfLock) {
+ pw.println();
+ pw.print(" #Failures: ");
+ pw.println(mWtfCount);
- if (mLastWtfStacktrace != null) {
- pw.print(" Last failure stack trace: ");
- pw.println(Log.getStackTraceString(mLastWtfStacktrace));
+ if (mLastWtfStacktrace != null) {
+ pw.print(" Last failure stack trace: ");
+ pw.println(Log.getStackTraceString(mLastWtfStacktrace));
+ }
}
pw.println();
@@ -5148,7 +5151,7 @@
if (e == null) {
e = new RuntimeException("Stacktrace");
}
- synchronized (mLock) {
+ synchronized (mWtfLock) {
mWtfCount++;
mLastWtfStacktrace = new Exception("Last failure was logged here:");
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 9c74dd7..a622d07 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -32,6 +32,15 @@
]
},
{
+ "file_patterns": ["(/|^)PackageManagerService\\.java","(/|^)UserManagerService\\.java"],
+ "name": "CtsAppCloningDeviceTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
"name": "CtsShortcutHostTestCases",
"file_patterns": ["(/|^)ShortcutService\\.java"]
},
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 8f89fdd..a1d5054 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -313,10 +313,29 @@
*/
public abstract @Nullable UserProperties getUserProperties(@UserIdInt int userId);
- /** TODO(b/239982558): add javadoc / mention invalid_id is used to unassing */
+ /**
+ * Assigns a user to a display.
+ *
+ * <p>On most devices this call will be a no-op, but it will be used on devices that support
+ * multiple users on multiple displays (like automotives with passenger displays).
+ */
public abstract void assignUserToDisplay(@UserIdInt int userId, int displayId);
/**
+ * Unassigns a user from its current display.
+ *
+ * <p>On most devices this call will be a no-op, but it will be used on devices that support
+ * multiple users on multiple displays (like automotives with passenger displays).
+ */
+ public abstract void unassignUserFromDisplay(@UserIdInt int userId);
+
+ /**
+ * Returns {@code true} if the user is visible (as defined by
+ * {@link UserManager#isUserVisible()}.
+ */
+ public abstract boolean isUserVisible(@UserIdInt int userId);
+
+ /**
* Returns {@code true} if the user is visible (as defined by
* {@link UserManager#isUserVisible()} in the given display.
*/
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3b898f8..4bcda2c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -173,6 +173,8 @@
private static final String LOG_TAG = "UserManagerService";
static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
+ // For Multiple Users on Multiple Displays
+ static final boolean DBG_MUMD = false; // DO NOT SUBMIT WITH TRUE
private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
// Can be used for manual testing of id recycling
private static final boolean RELEASE_DELETED_USER_ID = false; // DO NOT SUBMIT WITH TRUE
@@ -303,6 +305,7 @@
private PackageManagerInternal mPmInternal;
private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
+ private ActivityManagerInternal mAmInternal;
/** Indicates that this is the 1st boot after the system user mode was changed by emulation. */
private boolean mUpdatingSystemUserMode;
@@ -381,7 +384,7 @@
}
@GuardedBy("mUsersLock")
- private final SparseArray<UserData> mUsers = new SparseArray<>();
+ private final SparseArray<UserData> mUsers;
/**
* Map of user type names to their corresponding {@link UserTypeDetails}.
@@ -625,14 +628,13 @@
private final WatchedUserStates mUserStates = new WatchedUserStates();
/**
- * Used on devices that support background users (key) running on secondary displays (value).
- *
- * <p>Is {@code null} by default and instantiated on demand when the users are started on
- * secondary displays.
+ * Set on on devices that support background users (key) running on secondary displays (value).
*/
+ // TODO(b/239982558): move such logic to a different class (like UserDisplayAssigner)
@Nullable
- @GuardedBy("mUsersLock")
- private SparseIntArray mUsersOnSecondaryDisplays;
+ @GuardedBy("mUsersOnSecondaryDisplays")
+ private final SparseIntArray mUsersOnSecondaryDisplays;
+ private final boolean mUsersOnSecondaryDisplaysEnabled;
private static UserManagerService sInstance;
@@ -704,10 +706,11 @@
}
}
- // TODO b/28848102 Add support for test dependencies injection
+ // TODO(b/28848102) Add support for test dependencies injection
@VisibleForTesting
UserManagerService(Context context) {
- this(context, null, null, new Object(), context.getCacheDir());
+ this(context, /* pm= */ null, /* userDataPreparer= */ null,
+ /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null);
}
/**
@@ -717,14 +720,18 @@
*/
UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer,
Object packagesLock) {
- this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory());
+ this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory(),
+ /* users= */ null);
}
- private UserManagerService(Context context, PackageManagerService pm,
- UserDataPreparer userDataPreparer, Object packagesLock, File dataDir) {
+ @VisibleForTesting
+ UserManagerService(Context context, PackageManagerService pm,
+ UserDataPreparer userDataPreparer, Object packagesLock, File dataDir,
+ SparseArray<UserData> users) {
mContext = context;
mPm = pm;
mPackagesLock = packagesLock;
+ mUsers = users != null ? users : new SparseArray<>();
mHandler = new MainHandler();
mUserDataPreparer = userDataPreparer;
mUserTypes = UserTypeFactory.getUserTypes();
@@ -750,6 +757,8 @@
mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING);
mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null;
emulateSystemUserModeIfNeeded();
+ mUsersOnSecondaryDisplaysEnabled = UserManager.isUsersOnSecondaryDisplaysEnabled();
+ mUsersOnSecondaryDisplays = mUsersOnSecondaryDisplaysEnabled ? new SparseIntArray() : null;
}
void systemReady() {
@@ -1083,9 +1092,20 @@
@Override
public int getProfileParentId(@UserIdInt int userId) {
checkManageUsersPermission("get the profile parent");
- return mLocalService.getProfileParentId(userId);
+ return getProfileParentIdUnchecked(userId);
}
+ private @UserIdInt int getProfileParentIdUnchecked(@UserIdInt int userId) {
+ synchronized (mUsersLock) {
+ UserInfo profileParent = getProfileParentLU(userId);
+ if (profileParent == null) {
+ return userId;
+ }
+ return profileParent.id;
+ }
+ }
+
+
@GuardedBy("mUsersLock")
private UserInfo getProfileParentLU(@UserIdInt int userId) {
UserInfo profile = getUserInfoLU(userId);
@@ -1697,8 +1717,7 @@
+ ") is running in the foreground");
}
- int currentUser = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
- return currentUser == userId;
+ return userId == getCurrentUserId();
}
@Override
@@ -1720,15 +1739,15 @@
return true;
}
- // TODO(b/239824814): STOPSHIP - add CTS tests (requires change on testing infra)
- synchronized (mUsersLock) {
- if (mUsersOnSecondaryDisplays != null) {
- // TODO(b/239824814): make sure it handles profile as well
- return (mUsersOnSecondaryDisplays.indexOfKey(userId) >= 0);
- }
+ // Device doesn't support multiple users on multiple displays, so only users checked above
+ // can be visible
+ if (!mUsersOnSecondaryDisplaysEnabled) {
+ return false;
}
- return false;
+ synchronized (mUsersOnSecondaryDisplays) {
+ return mUsersOnSecondaryDisplays.indexOfKey(userId) >= 0;
+ }
}
// TODO(b/239982558): add unit test
@@ -1736,22 +1755,49 @@
if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
return Display.DEFAULT_DISPLAY;
}
- synchronized (mUsersLock) {
- return mUsersOnSecondaryDisplays == null
- ? Display.INVALID_DISPLAY
- : mUsersOnSecondaryDisplays.get(userId, Display.INVALID_DISPLAY);
+
+ if (!mUsersOnSecondaryDisplaysEnabled) {
+ return Display.INVALID_DISPLAY;
+ }
+
+ synchronized (mUsersOnSecondaryDisplays) {
+ return mUsersOnSecondaryDisplays.get(userId, Display.INVALID_DISPLAY);
}
}
- private boolean isCurrentUserOrRunningProfileOfCurrentUser(@UserIdInt int userId) {
- int currentUserId = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
+ /**
+ * Gets the current user id, calling {@link ActivityManagerInternal} directly (and without
+ * performing any permission check).
+ *
+ * @return id of current foreground user, or {@link UserHandle#USER_NULL} if
+ * {@link ActivityManagerInternal} is not available yet.
+ */
+ @VisibleForTesting
+ int getCurrentUserId() {
+ ActivityManagerInternal activityManagerInternal = getActivityManagerInternal();
+ if (activityManagerInternal == null) {
+ Slog.w(LOG_TAG, "getCurrentUserId() called too early, ActivityManagerInternal"
+ + " is not set yet");
+ return UserHandle.USER_NULL;
+ }
+ return activityManagerInternal.getCurrentUserId();
+ }
+
+ /**
+ * Gets whether the user is the current foreground user or a started profile of that user.
+ *
+ * <p>Doesn't perform any permission check.
+ */
+ @VisibleForTesting
+ boolean isCurrentUserOrRunningProfileOfCurrentUser(@UserIdInt int userId) {
+ int currentUserId = getCurrentUserId();
if (currentUserId == userId) {
return true;
}
- if (isProfile(userId)) {
- int parentId = Binder.withCleanCallingIdentity(() -> getProfileParentId(userId));
+ if (isProfileUnchecked(userId)) {
+ int parentId = getProfileParentIdUnchecked(userId);
if (parentId == currentUserId) {
return isUserRunning(userId);
}
@@ -1760,18 +1806,27 @@
return false;
}
- // TODO(b/239982558): currently used just by shell command, might need to move to
- // UserManagerInternal if needed by other components (like WindowManagerService)
- // TODO(b/239982558): add unit test
- // TODO(b/239982558): try to merge with isUserVisibleUnchecked()
+ // TODO(b/239982558): try to merge with isUserVisibleUnchecked() (once both are unit tested)
boolean isUserVisibleOnDisplay(@UserIdInt int userId, int displayId) {
+ // TODO(b/244644281): temporary workaround to let WM use this API without breaking current
+ // behavior (otherwise current user / profiles wouldn't be able to launch activities on
+ // other non-passenger displays, like cluster, display, or virtual displays)
+ if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
+ return true;
+ }
+
if (displayId == Display.DEFAULT_DISPLAY) {
return isCurrentUserOrRunningProfileOfCurrentUser(userId);
}
- synchronized (mUsersLock) {
- // TODO(b/239824814): make sure it handles profile as well
- return mUsersOnSecondaryDisplays != null && mUsersOnSecondaryDisplays.get(userId,
- Display.INVALID_DISPLAY) == displayId;
+
+ // Device doesn't support multiple users on multiple displays, so only users checked above
+ // can be visible
+ if (!mUsersOnSecondaryDisplaysEnabled) {
+ return false;
+ }
+
+ synchronized (mUsersOnSecondaryDisplays) {
+ return mUsersOnSecondaryDisplays.get(userId, Display.INVALID_DISPLAY) == displayId;
}
}
@@ -2613,6 +2668,11 @@
if (!UserRestrictionsUtils.isValidRestriction(key)) {
return;
}
+
+ if (!userExists(userId)) {
+ throw new IllegalArgumentException("Cannot set user restriction. "
+ + "User with this id does not exist");
+ }
synchronized (mRestrictionsLock) {
// Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
// a copy.
@@ -3197,6 +3257,22 @@
}
/**
+ * Checks whether user with a given ID exists.
+ * @param id User id to be checked.
+ */
+ @VisibleForTesting
+ boolean userExists(int id) {
+ synchronized (mUsersLock) {
+ for (int userId : mUserIds) {
+ if (userId == id) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns an array of user ids, including pre-created users.
*
* <p>This method should only used for the specific cases that need to handle pre-created users;
@@ -3719,7 +3795,6 @@
}
}
- updateUserIds();
initDefaultGuestRestrictions();
writeUserLP(userData);
@@ -4916,6 +4991,7 @@
synchronized (mUsersLock) {
mUsers.put(userInfo.id, userData);
}
+ updateUserIds();
return userData;
}
@@ -5063,7 +5139,7 @@
final long ident = Binder.clearCallingIdentity();
try {
final UserData userData;
- int currentUser = ActivityManager.getCurrentUser();
+ int currentUser = getCurrentUserId();
if (currentUser == userId) {
Slog.w(LOG_TAG, "Current user cannot be removed.");
return false;
@@ -5191,7 +5267,7 @@
}
// Attempt to immediately remove a non-current user
- final int currentUser = ActivityManager.getCurrentUser();
+ final int currentUser = getCurrentUserId();
if (currentUser != userId) {
// Attempt to remove the user. This will fail if the user is the current user
if (removeUserUnchecked(userId)) {
@@ -5992,11 +6068,10 @@
return;
}
- final ActivityManagerInternal amInternal = LocalServices
- .getService(ActivityManagerInternal.class);
+ final int currentUserId = getCurrentUserId();
pw.print("Current user: ");
- if (amInternal != null) {
- pw.println(amInternal.getCurrentUserId());
+ if (currentUserId != UserHandle.USER_NULL) {
+ pw.println(currentUserId);
} else {
pw.println("N/A");
}
@@ -6054,11 +6129,14 @@
} // synchronized (mPackagesLock)
// Multiple Users on Multiple Display info
- pw.println(" Supports users on secondary displays: "
+ pw.println(" Supports users on secondary displays: " + mUsersOnSecondaryDisplaysEnabled);
+ // mUsersOnSecondaryDisplaysEnabled is set on constructor, while the UserManager API is
+ // set dynamically, so print both to help cases where the developer changed it on the fly
+ pw.println(" UM.isUsersOnSecondaryDisplaysEnabled(): "
+ UserManager.isUsersOnSecondaryDisplaysEnabled());
- synchronized (mUsersLock) {
- if (mUsersOnSecondaryDisplays != null) {
- pw.print(" Users on secondary displays: ");
+ if (mUsersOnSecondaryDisplaysEnabled) {
+ pw.print(" Users on secondary displays: ");
+ synchronized (mUsersOnSecondaryDisplays) {
pw.println(mUsersOnSecondaryDisplays);
}
}
@@ -6120,13 +6198,13 @@
private void dumpUser(PrintWriter pw, @UserIdInt int userId, StringBuilder sb, long now,
long nowRealtime) {
if (userId == UserHandle.USER_CURRENT) {
- final ActivityManagerInternal amInternal = LocalServices
- .getService(ActivityManagerInternal.class);
- if (amInternal == null) {
+ final int currentUserId = getCurrentUserId();
+ pw.print("Current user: ");
+ if (currentUserId == UserHandle.USER_NULL) {
pw.println("Cannot determine current user");
return;
}
- userId = amInternal.getCurrentUserId();
+ userId = currentUserId;
}
synchronized (mUsersLock) {
@@ -6372,7 +6450,7 @@
@Override
public void removeAllUsers() {
- if (UserHandle.USER_SYSTEM == ActivityManager.getCurrentUser()) {
+ if (UserHandle.USER_SYSTEM == getCurrentUserId()) {
// Remove the non-system users straight away.
removeNonSystemUsers();
} else {
@@ -6554,13 +6632,7 @@
@Override
public int getProfileParentId(@UserIdInt int userId) {
- synchronized (mUsersLock) {
- UserInfo profileParent = getProfileParentLU(userId);
- if (profileParent == null) {
- return userId;
- }
- return profileParent.id;
- }
+ return getProfileParentIdUnchecked(userId);
}
@Override
@@ -6628,44 +6700,32 @@
@Override
public void assignUserToDisplay(int userId, int displayId) {
- if (DBG) {
- Slogf.d(LOG_TAG, "assignUserToDisplay(%d, %d): mUsersOnSecondaryDisplays=%s",
- userId, displayId, mUsersOnSecondaryDisplays);
+ if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "assignUserToDisplay(%d, %d)", userId, displayId);
}
- // TODO(b/240613396) throw exception if feature not supported
- if (displayId == Display.INVALID_DISPLAY) {
- synchronized (mUsersLock) {
- if (mUsersOnSecondaryDisplays == null) {
- if (false) {
- // TODO(b/240613396): remove this if once we check for support above
- Slogf.wtf(LOG_TAG, "assignUserToDisplay(%d, %d): no "
- + "mUsersOnSecondaryDisplays", userId, displayId);
- }
- return;
- }
- if (DBG) {
- Slogf.d(LOG_TAG, "Removing %d from mUsersOnSecondaryDisplays", userId);
- }
- mUsersOnSecondaryDisplays.delete(userId);
- if (mUsersOnSecondaryDisplays.size() == 0) {
- if (DBG) {
- Slogf.d(LOG_TAG, "Removing mUsersOnSecondaryDisplays");
- }
- mUsersOnSecondaryDisplays = null;
- }
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ // Don't need to do anything because methods (such as isUserVisible()) already know
+ // that the current user (and their profiles) is assigned to the default display.
+ if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "ignoring on default display");
}
return;
}
- synchronized (mUsersLock) {
- if (mUsersOnSecondaryDisplays == null) {
- if (DBG) {
- Slogf.d(LOG_TAG, "Creating mUsersOnSecondaryDisplays");
- }
- mUsersOnSecondaryDisplays = new SparseIntArray();
- }
- if (DBG) {
+ if (!mUsersOnSecondaryDisplaysEnabled) {
+ throw new UnsupportedOperationException("assignUserToDisplay(" + userId + ", "
+ + displayId + ") called on device that doesn't support multiple "
+ + "users on multiple displays");
+ }
+
+ Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot start system user"
+ + " on secondary display (%d)", displayId);
+ // TODO(b/239982558): call DisplayManagerInternal to check if display is valid instead
+ Preconditions.checkArgument(displayId > 0, "Invalid display id (%d)", displayId);
+
+ synchronized (mUsersOnSecondaryDisplays) {
+ if (DBG_MUMD) {
Slogf.d(LOG_TAG, "Adding %d->%d to mUsersOnSecondaryDisplays",
userId, displayId);
}
@@ -6704,6 +6764,34 @@
}
@Override
+ public void unassignUserFromDisplay(@UserIdInt int userId) {
+ if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "unassignUserFromDisplay(%d)", userId);
+ }
+ if (!mUsersOnSecondaryDisplaysEnabled) {
+ // Don't need to do anything because methods (such as isUserVisible()) already know
+ // that the current user (and their profiles) is assigned to the default display.
+ if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "ignoring when device doesn't support MUMD");
+ }
+ return;
+ }
+
+ synchronized (mUsersOnSecondaryDisplays) {
+ if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId,
+ mUsersOnSecondaryDisplays);
+ }
+ mUsersOnSecondaryDisplays.delete(userId);
+ }
+ }
+
+ @Override
+ public boolean isUserVisible(int userId) {
+ return isUserVisibleUnchecked(userId);
+ }
+
+ @Override
public boolean isUserVisible(int userId, int displayId) {
return isUserVisibleOnDisplay(userId, displayId);
}
@@ -6873,4 +6961,12 @@
}
return mDevicePolicyManagerInternal;
}
+
+ /** Returns the internal activity manager interface. */
+ private @Nullable ActivityManagerInternal getActivityManagerInternal() {
+ if (mAmInternal == null) {
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ }
+ return mAmInternal;
+ }
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index e0d5aec..6caddaf 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -34,6 +34,7 @@
import android.util.Slog;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.util.ArrayUtils;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.PackageImpl;
@@ -151,6 +152,12 @@
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,
List<File> frameworkSplits) throws PackageManagerException {
+ var files = packageFile.listFiles();
+ // Apk directory is directly nested under the current directory
+ if (ArrayUtils.size(files) == 1 && files[0].isDirectory()) {
+ packageFile = files[0];
+ }
+
if (useCaches && mCacher != null) {
ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
if (parsed != null) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 33b32f8..11fb78f 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -545,6 +545,9 @@
for (int i = component.getIntents().size() - 1; i >= 0; i--) {
IntentFilter filter = component.getIntents().get(i).getIntentFilter();
for (int groupIndex = filter.countMimeGroups() - 1; groupIndex >= 0; groupIndex--) {
+ if (mimeGroups != null && mimeGroups.size() > 500) {
+ throw new IllegalStateException("Max limit on number of MIME Groups reached");
+ }
mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 164445c..c05d5ce 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -2692,7 +2692,6 @@
final Permission bp = mRegistry.getPermission(permName);
final boolean appSupportsRuntimePermissions =
pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
- String legacyActivityRecognitionPermission = null;
if (DEBUG_INSTALL && bp != null) {
Log.i(TAG, "Package " + friendlyName
@@ -2716,47 +2715,12 @@
// Cache newImplicitPermissions before modifing permissionsState as for the
// shared uids the original and new state are the same object
if (!origState.hasPermissionState(permName)
- && (pkg.getImplicitPermissions().contains(permName)
- || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
- if (pkg.getImplicitPermissions().contains(permName)) {
+ && (pkg.getImplicitPermissions().contains(permName))) {
// If permName is an implicit permission, try to auto-grant
newImplicitPermissions.add(permName);
-
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, permName + " is newly added for " + friendlyName);
}
- } else {
- // Special case for Activity Recognition permission. Even if AR
- // permission is not an implicit permission we want to add it to the
- // list (try to auto-grant it) if the app was installed on a device
- // before AR permission was split, regardless of if the app now requests
- // the new AR permission or has updated its target SDK and AR is no
- // longer implicit to it. This is a compatibility workaround for apps
- // when AR permission was split in Q.
- // TODO(zhanghai): This calls into SystemConfig, which generally
- // shouldn't cause deadlock, but maybe we should keep a cache of the
- // split permission list and just eliminate the possibility.
- final List<PermissionManager.SplitPermissionInfo> permissionList =
- getSplitPermissionInfos();
- int numSplitPerms = permissionList.size();
- for (int splitPermNum = 0; splitPermNum < numSplitPerms;
- splitPermNum++) {
- PermissionManager.SplitPermissionInfo sp = permissionList.get(
- splitPermNum);
- String splitPermName = sp.getSplitPermission();
- if (sp.getNewPermissions().contains(permName)
- && origState.isPermissionGranted(splitPermName)) {
- legacyActivityRecognitionPermission = splitPermName;
- newImplicitPermissions.add(permName);
-
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for "
- + friendlyName);
- }
- break;
- }
- }
- }
}
// TODO(b/140256621): The package instant app method has been removed
@@ -2890,8 +2854,7 @@
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
|| (!hardRestricted || restrictionExempt)) {
- if ((origPermState != null && origPermState.isGranted())
- || legacyActivityRecognitionPermission != null) {
+ if ((origPermState != null && origPermState.isGranted())) {
if (!uidState.grantPermission(bp)) {
wasChanged = true;
}
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 9213c87..9dfaca8 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -106,16 +106,43 @@
return KeyEvent.keyCodeToString(mKeyCode1) + " + "
+ KeyEvent.keyCodeToString(mKeyCode2);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o instanceof TwoKeysCombinationRule) {
+ TwoKeysCombinationRule that = (TwoKeysCombinationRule) o;
+ return (mKeyCode1 == that.mKeyCode1 && mKeyCode2 == that.mKeyCode2) || (
+ mKeyCode1 == that.mKeyCode2 && mKeyCode2 == that.mKeyCode1);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mKeyCode1;
+ result = 31 * result + mKeyCode2;
+ return result;
+ }
}
- public KeyCombinationManager(Handler handler) {
+ KeyCombinationManager(Handler handler) {
mHandler = handler;
}
void addRule(TwoKeysCombinationRule rule) {
+ if (mRules.contains(rule)) {
+ throw new IllegalArgumentException("Rule : " + rule + " already exists.");
+ }
mRules.add(rule);
}
+ void removeRule(TwoKeysCombinationRule rule) {
+ mRules.remove(rule);
+ }
+
/**
* Check if the key event could be intercepted by combination key rule before it is dispatched
* to a window.
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index f00edf3..92f00113 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -151,6 +151,23 @@
+ ", VeryLongPress=" + supportVeryLongPress()
+ ", MaxMultiPressCount=" + getMaxMultiPressCount();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o instanceof SingleKeyRule) {
+ SingleKeyRule that = (SingleKeyRule) o;
+ return mKeyCode == that.mKeyCode;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mKeyCode;
+ }
}
static SingleKeyGestureDetector get(Context context) {
@@ -167,9 +184,16 @@
}
void addRule(SingleKeyRule rule) {
+ if (mRules.contains(rule)) {
+ throw new IllegalArgumentException("Rule : " + rule + " already exists.");
+ }
mRules.add(rule);
}
+ void removeRule(SingleKeyRule rule) {
+ mRules.remove(rule);
+ }
+
void interceptKey(KeyEvent event, boolean interactive) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
// Store the non interactive state when first down.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 53b8b53..690dd10 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -21,7 +21,6 @@
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
import android.view.InsetsState.InternalInsetsType;
import android.view.InsetsVisibilities;
import android.view.WindowInsetsController.Appearance;
@@ -162,11 +161,6 @@
boolean requestWindowMagnificationConnection(boolean request);
/**
- * Handles a logging command from the WM shell command.
- */
- void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd);
-
- /**
* @see com.android.internal.statusbar.IStatusBar#setNavigationBarLumaSamplingEnabled(int,
* boolean)
*/
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index bec3754..653b51a9 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -62,7 +62,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -664,15 +663,6 @@
}
@Override
- public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {
- if (mBar != null) {
- try {
- mBar.handleWindowManagerLoggingCommand(args, outFd);
- } catch (RemoteException ex) { }
- }
- }
-
- @Override
public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
if (mBar != null) {
try {
diff --git a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
index 2c6fbbc9..fd1a3ac 100644
--- a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
+++ b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
@@ -192,41 +192,44 @@
// delivered asynchronously but enqueued until the step processing is finished.
boolean hasPrepared = false;
boolean hasTriggered = false;
+ boolean hasFailed = false;
long maxDuration = 0;
- try {
- hasPrepared = conductor.vibratorManagerHooks.prepareSyncedVibration(
- effectMapping.getRequiredSyncCapabilities(),
- effectMapping.getVibratorIds());
+ hasPrepared = conductor.vibratorManagerHooks.prepareSyncedVibration(
+ effectMapping.getRequiredSyncCapabilities(),
+ effectMapping.getVibratorIds());
- for (AbstractVibratorStep step : steps) {
- long duration = startVibrating(step, nextSteps);
- if (duration < 0) {
- // One vibrator has failed, fail this entire sync attempt.
- return maxDuration = -1;
- }
- maxDuration = Math.max(maxDuration, duration);
+ for (AbstractVibratorStep step : steps) {
+ long duration = startVibrating(step, nextSteps);
+ if (duration < 0) {
+ // One vibrator has failed, fail this entire sync attempt.
+ hasFailed = true;
+ break;
}
+ maxDuration = Math.max(maxDuration, duration);
+ }
- // Check if sync was prepared and if any step was accepted by a vibrator,
- // otherwise there is nothing to trigger here.
- if (hasPrepared && maxDuration > 0) {
- hasTriggered = conductor.vibratorManagerHooks.triggerSyncedVibration(
- getVibration().id);
- }
- return maxDuration;
- } finally {
- if (hasPrepared && !hasTriggered) {
- // Trigger has failed or all steps were ignored by the vibrators.
- conductor.vibratorManagerHooks.cancelSyncedVibration();
- nextSteps.clear();
- } else if (maxDuration < 0) {
- // Some vibrator failed without being prepared so other vibrators might be
- // active. Cancel and remove every pending step from output list.
- for (int i = nextSteps.size() - 1; i >= 0; i--) {
- nextSteps.remove(i).cancelImmediately();
- }
+ // Check if sync was prepared and if any step was accepted by a vibrator,
+ // otherwise there is nothing to trigger here.
+ if (hasPrepared && !hasFailed && maxDuration > 0) {
+ hasTriggered = conductor.vibratorManagerHooks.triggerSyncedVibration(getVibration().id);
+ hasFailed &= hasTriggered;
+ }
+
+ if (hasFailed) {
+ // Something failed, possibly after other vibrators were activated.
+ // Cancel and remove every pending step from output list.
+ for (int i = nextSteps.size() - 1; i >= 0; i--) {
+ nextSteps.remove(i).cancelImmediately();
}
}
+
+ // Cancel the preparation if trigger failed or all
+ if (hasPrepared && !hasTriggered) {
+ // Trigger has failed or was skipped, so abort the synced vibration.
+ conductor.vibratorManagerHooks.cancelSyncedVibration();
+ }
+
+ return hasFailed ? -1 : maxDuration;
}
private long startVibrating(AbstractVibratorStep step, List<Step> nextSteps) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 63dcbf8..be80118 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -357,7 +357,6 @@
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
-import com.android.server.wm.utils.WmDisplayCutout;
import dalvik.annotation.optimization.NeverCompile;
@@ -917,7 +916,6 @@
*/
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
- private final Rect mTmpOutNonDecorBounds = new Rect();
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
@@ -8143,10 +8141,12 @@
final int orientation = parentBounds.height() >= parentBounds.width()
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
// Compute orientation from stable parent bounds (= parent bounds with insets applied)
+ final DisplayInfo di = isFixedRotationTransforming()
+ ? getFixedRotationTransformDisplayInfo()
+ : mDisplayContent.getDisplayInfo();
final Task task = getTask();
- task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */,
- outStableBounds /* outStableBounds */, parentBounds /* bounds */,
- mDisplayContent.getDisplayInfo());
+ task.calculateInsetFrames(mTmpBounds /* outNonDecorBounds */,
+ outStableBounds /* outStableBounds */, parentBounds /* bounds */, di);
final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width()
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
// If orientation does not match the orientation with insets applied, then a
@@ -9708,10 +9708,10 @@
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final int dw = rotated ? display.mBaseDisplayHeight : display.mBaseDisplayWidth;
final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
- final WmDisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation);
- policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
- mStableInsets[rotation].set(mNonDecorInsets[rotation]);
- policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
+ final DisplayPolicy.DecorInsets.Info decorInfo =
+ policy.getDecorInsetsInfo(rotation, dw, dh);
+ mNonDecorInsets[rotation].set(decorInfo.mNonDecorInsets);
+ mStableInsets[rotation].set(decorInfo.mConfigInsets);
if (unfilledContainerBounds == null) {
continue;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 8f18064..037cd8e 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -97,6 +97,8 @@
/** Whether an {@link ActivityStarter} is currently executing (starting an Activity). */
private boolean mInExecution = false;
+ private final BackgroundActivityStartController mBalController;
+
/**
* TODO(b/64750076): Capture information necessary for dump and
* {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
@@ -119,6 +121,7 @@
mFactory.setController(this);
mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service.mGlobalLock,
service.mH);
+ mBalController = new BackgroundActivityStartController(mService, mSupervisor);
}
/**
@@ -632,4 +635,8 @@
pw.println("(nothing)");
}
}
+
+ BackgroundActivityStartController getBackgroundActivityLaunchController() {
+ return mBalController;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f224cc7..bc1c6b2 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.ActivityManager.START_ABORTED;
import static android.app.ActivityManager.START_CANCELED;
@@ -52,7 +51,6 @@
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static android.content.pm.ActivityInfo.launchModeToString;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -61,7 +59,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
@@ -72,8 +69,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.ActivityTaskManagerService.ANIMATE;
-import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
-import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -98,7 +93,6 @@
import android.app.WindowConfiguration;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
-import android.content.ComponentName;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
@@ -114,15 +108,12 @@
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.Process;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.voice.IVoiceInteractionSession;
import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.DebugUtils;
import android.util.Pools.SynchronizedPool;
import android.util.Slog;
import android.window.RemoteTransition;
@@ -203,6 +194,10 @@
private TaskFragment mAddingToTaskFragment;
@VisibleForTesting
boolean mAddingToTask;
+ // Activity that was moved to the top of its task in situations where activity-order changes
+ // due to launch flags (eg. REORDER_TO_TOP).
+ @VisibleForTesting
+ ActivityRecord mMovedToTopActivity;
private Task mSourceRootTask;
private Task mTargetRootTask;
@@ -259,8 +254,6 @@
/**
* Generates an {@link ActivityStarter} that is ready to handle a new start request.
- * @param controller The {@link ActivityStartController} which the starter who will own
- * this instance.
* @return an {@link ActivityStarter}
*/
ActivityStarter obtain();
@@ -1033,10 +1026,20 @@
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
"shouldAbortBackgroundActivityStart");
- restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
- callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
- request.originatingPendingIntent, request.allowBackgroundActivityStart,
- intent, checkedOptions);
+ BackgroundActivityStartController balController =
+ mController.getBackgroundActivityLaunchController();
+ restrictedBgActivity =
+ balController.shouldAbortBackgroundActivityStart(
+ callingUid,
+ callingPid,
+ callingPackage,
+ realCallingUid,
+ realCallingPid,
+ callerApp,
+ request.originatingPendingIntent,
+ request.allowBackgroundActivityStart,
+ intent,
+ checkedOptions);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
@@ -1270,282 +1273,6 @@
mController.onExecutionStarted();
}
- private boolean isHomeApp(int uid, @Nullable String packageName) {
- if (mService.mHomeProcess != null) {
- // Fast check
- return uid == mService.mHomeProcess.mUid;
- }
- if (packageName == null) {
- return false;
- }
- ComponentName activity =
- mService.getPackageManagerInternalLocked().getDefaultHomeActivity(
- UserHandle.getUserId(uid));
- return activity != null && packageName.equals(activity.getPackageName());
- }
-
- boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
- final String callingPackage, int realCallingUid, int realCallingPid,
- WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart, Intent intent, ActivityOptions checkedOptions) {
- // don't abort for the most important UIDs
- final int callingAppId = UserHandle.getAppId(callingUid);
- final boolean useCallingUidState =
- originatingPendingIntent == null || checkedOptions == null
- || !checkedOptions.getIgnorePendingIntentCreatorForegroundState();
- if (useCallingUidState) {
- if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
- || callingAppId == Process.NFC_UID) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG,
- "Activity start allowed for important callingUid (" + callingUid + ")");
- }
- return false;
- }
-
- // Always allow home application to start activities.
- if (isHomeApp(callingUid, callingPackage)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG,
- "Activity start allowed for home app callingUid (" + callingUid + ")");
- }
- return false;
- }
-
- // IME should always be allowed to start activity, like IME settings.
- final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();
- if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");
- }
- return false;
- }
- }
-
- // This is used to block background activity launch even if the app is still
- // visible to user after user clicking home button.
- final int appSwitchState = mService.getBalAppSwitchesState();
-
- // don't abort if the callingUid has a visible window or is a persistent system process
- final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
- final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
- final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
- || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
- || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
- final boolean isCallingUidPersistentSystemProcess =
- callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-
- // Normal apps with visible app window will be allowed to start activity if app switching
- // is allowed, or apps like live wallpaper with non app visible window will be allowed.
- final boolean appSwitchAllowedOrFg =
- appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
- final boolean allowCallingUidStartActivity =
- ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
- && callingUidHasAnyVisibleWindow)
- || isCallingUidPersistentSystemProcess;
- if (useCallingUidState && allowCallingUidStartActivity) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid
- + ", isCallingUidPersistentSystemProcess = "
- + isCallingUidPersistentSystemProcess);
- }
- return false;
- }
- // take realCallingUid into consideration
- final int realCallingUidProcState = (callingUid == realCallingUid)
- ? callingUidProcState
- : mService.mActiveUids.getUidState(realCallingUid);
- final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
- ? callingUidHasAnyVisibleWindow
- : mService.hasActiveVisibleWindow(realCallingUid);
- final boolean isRealCallingUidForeground = (callingUid == realCallingUid)
- ? isCallingUidForeground
- : realCallingUidHasAnyVisibleWindow
- || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
- final int realCallingAppId = UserHandle.getAppId(realCallingUid);
- final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid)
- ? isCallingUidPersistentSystemProcess
- : (realCallingAppId == Process.SYSTEM_UID)
- || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-
- // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
- // visible window.
- if (Process.isSdkSandboxUid(realCallingUid)) {
- int realCallingSdkSandboxUidToAppUid = Process.getAppUidForSdkSandboxUid(
- UserHandle.getAppId(realCallingUid));
-
- if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start allowed: uid in SDK sandbox ("
- + realCallingUid + ") has visible (non-toast) window.");
- }
- return false;
- }
- }
-
- // Legacy behavior allows to use caller foreground state to bypass BAL restriction.
- final boolean balAllowedByPiSender =
- PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
-
- if (balAllowedByPiSender && realCallingUid != callingUid) {
- final boolean useCallerPermission =
- PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions);
- if (useCallerPermission && ActivityManager.checkComponentPermission(
- android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
- realCallingUid, -1, true)
- == PackageManager.PERMISSION_GRANTED) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
- + ") has BAL permission.");
- }
- return false;
- }
-
- // don't abort if the realCallingUid has a visible window
- // TODO(b/171459802): We should check appSwitchAllowed also
- if (realCallingUidHasAnyVisibleWindow) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
- + ") has visible (non-toast) window");
- }
- return false;
- }
- // if the realCallingUid is a persistent system process, abort if the IntentSender
- // wasn't allowed to start an activity
- if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
- + ") is persistent system process AND intent sender allowed "
- + "(allowBackgroundActivityStart = true)");
- }
- return false;
- }
- // don't abort if the realCallingUid is an associated companion app
- if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid),
- realCallingUid)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
- + ") is companion app");
- }
- return false;
- }
- }
- if (useCallingUidState) {
- // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
- if (mService.checkPermission(
- START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
- == PERMISSION_GRANTED) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG,
- "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND "
- + "permission granted for uid "
- + callingUid);
- }
- return false;
- }
- // don't abort if the caller has the same uid as the recents component
- if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
- + ") is recents");
- }
- return false;
- }
- // don't abort if the callingUid is the device owner
- if (mService.isDeviceOwner(callingUid)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
- + ") is device owner");
- }
- return false;
- }
- // don't abort if the callingUid has companion device
- final int callingUserId = UserHandle.getUserId(callingUid);
- if (mService.isAssociatedCompanionApp(callingUserId,
- callingUid)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
- + ") is companion app");
- }
- return false;
- }
- // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
- if (mService.hasSystemAlertWindowPermission(callingUid,
- callingPid, callingPackage)) {
- Slog.w(TAG, "Background activity start for " + callingPackage
- + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
- return false;
- }
- }
- // If we don't have callerApp at this point, no caller was provided to startActivity().
- // That's the case for PendingIntent-based starts, since the creator's process might not be
- // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
- // caller if caller allows, so that we can make the decision based on its state.
- int callerAppUid = callingUid;
- if (callerApp == null && balAllowedByPiSender) {
- callerApp = mService.getProcessController(realCallingPid, realCallingUid);
- callerAppUid = realCallingUid;
- }
- // don't abort if the callerApp or other processes of that uid are allowed in any way
- if (callerApp != null && useCallingUidState) {
- // first check the original calling process
- if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
- + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
- }
- return false;
- }
- // only if that one wasn't allowed, check the other ones
- final ArraySet<WindowProcessController> uidProcesses =
- mService.mProcessMap.getProcesses(callerAppUid);
- if (uidProcesses != null) {
- for (int i = uidProcesses.size() - 1; i >= 0; i--) {
- final WindowProcessController proc = uidProcesses.valueAt(i);
- if (proc != callerApp
- && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG,
- "Background activity start allowed: process " + proc.getPid()
- + " from uid " + callerAppUid + " is allowed");
- }
- return false;
- }
- }
- }
- }
- // anything that has fallen through would currently be aborted
- Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
- + "; callingUid: " + callingUid
- + "; appSwitchState: " + appSwitchState
- + "; isCallingUidForeground: " + isCallingUidForeground
- + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow
- + "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
- "PROCESS_STATE_", callingUidProcState)
- + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
- + "; realCallingUid: " + realCallingUid
- + "; isRealCallingUidForeground: " + isRealCallingUidForeground
- + "; realCallingUidHasAnyVisibleWindow: " + realCallingUidHasAnyVisibleWindow
- + "; realCallingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
- "PROCESS_STATE_", realCallingUidProcState)
- + "; isRealCallingUidPersistentSystemProcess: "
- + isRealCallingUidPersistentSystemProcess
- + "; originatingPendingIntent: " + originatingPendingIntent
- + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart
- + "; intent: " + intent
- + "; callerApp: " + callerApp
- + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask())
- + "]");
- // log aborted activity start to TRON
- if (mService.isActivityStartsLoggingEnabled()) {
- mSupervisor.getActivityMetricsLogger().logAbortedBgActivityStart(intent, callerApp,
- callingUid, callingPackage, callingUidProcState, callingUidHasAnyVisibleWindow,
- realCallingUid, realCallingUidProcState, realCallingUidHasAnyVisibleWindow,
- (originatingPendingIntent != null));
- }
- return true;
- }
-
/**
* Creates a launch intent for the given auxiliary resolution data.
*/
@@ -1761,7 +1488,9 @@
// The activity is started new rather than just brought forward, so record it as an
// existence change.
transitionController.collectExistenceChange(started);
- } else if (result == START_DELIVERED_TO_TOP && newTransition != null) {
+ } else if (result == START_DELIVERED_TO_TOP && newTransition != null
+ // An activity has changed order/visibility so this isn't just deliver-to-top
+ && mMovedToTopActivity == null) {
// We just delivered to top, so there isn't an actual transition here.
if (!forceTransientTransition) {
newTransition.abort();
@@ -2348,10 +2077,15 @@
// In this situation we want to remove all activities from the task up to the one
// being started. In most cases this means we are resetting the task to its initial
// state.
+ int[] finishCount = new int[1];
final ActivityRecord clearTop = targetTask.performClearTop(mStartActivity,
- mLaunchFlags);
+ mLaunchFlags, finishCount);
if (clearTop != null && !clearTop.finishing) {
+ if (finishCount[0] > 0) {
+ // Only record if actually moved to top.
+ mMovedToTopActivity = clearTop;
+ }
if (clearTop.isRootOfTask()) {
// Activity aliases may mean we use different intents for the top activity,
// so make sure the task now has the identity of the new intent.
@@ -2384,10 +2118,15 @@
// already be running somewhere in the history, and we want to shuffle it to
// the front of the root task if so.
final ActivityRecord act =
- targetTask.findActivityInHistory(mStartActivity.mActivityComponent);
+ targetTask.findActivityInHistory(mStartActivity.mActivityComponent,
+ mStartActivity.mUserId);
if (act != null) {
final Task task = act.getTask();
- task.moveActivityToFrontLocked(act);
+ boolean actuallyMoved = task.moveActivityToFrontLocked(act);
+ if (actuallyMoved) {
+ // Only record if the activity actually moved.
+ mMovedToTopActivity = act;
+ }
act.updateOptionsLocked(mOptions);
deliverNewIntent(act, intentGrants);
act.getTaskFragment().clearLastPausedActivity();
@@ -2988,10 +2727,7 @@
newParent = candidateTf;
}
}
- if (newParent.canHaveEmbeddingActivityTransition(mStartActivity)) {
- // Make sure the embedded TaskFragment is included in the start activity transition.
- newParent.collectEmbeddedTaskFragmentIfNeeded();
- }
+ newParent.mTransitionController.collect(newParent);
if (mStartActivity.getTaskFragment() == null
|| mStartActivity.getTaskFragment() == newParent) {
newParent.addChild(mStartActivity, POSITION_TOP);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index be155a7..bdbe787 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -330,6 +330,7 @@
public static final String DUMP_RECENTS_CMD = "recents";
public static final String DUMP_RECENTS_SHORT_CMD = "r";
public static final String DUMP_TOP_RESUMED_ACTIVITY = "top-resumed";
+ public static final String DUMP_VISIBLE_ACTIVITIES = "visible";
/** This activity is not being relaunched, or being relaunched for a non-resize reason. */
public static final int RELAUNCH_REASON_NONE = 0;
@@ -2179,10 +2180,19 @@
if (appThread != null) {
callerApp = getProcessController(appThread);
}
- final ActivityStarter starter = getActivityStartController().obtainStarter(
- null /* intent */, "moveTaskToFront");
- if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,
- -1, callerApp, null, false, null, null)) {
+ final BackgroundActivityStartController balController =
+ getActivityStartController().getBackgroundActivityLaunchController();
+ if (balController.shouldAbortBackgroundActivityStart(
+ callingUid,
+ callingPid,
+ callingPackage,
+ -1,
+ -1,
+ callerApp,
+ null,
+ false,
+ null,
+ null)) {
if (!isBackgroundActivityStartsEnabled()) {
return;
}
@@ -4052,6 +4062,25 @@
}
}
+ void dumpVisibleActivitiesLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER VISIBLE ACTIVITIES (dumpsys activity visible)");
+ ArrayList<ActivityRecord> activities =
+ mRootWindowContainer.getDumpActivities("all", /* dumpVisibleRootTasksOnly */ true,
+ /* dumpFocusedRootTaskOnly */ false, UserHandle.USER_ALL);
+ boolean needSeparator = false;
+ for (int i = activities.size() - 1; i >= 0; i--) {
+ ActivityRecord activity = activities.get(i);
+ if (!activity.isVisible()) {
+ continue;
+ }
+ if (needSeparator) {
+ pw.println();
+ }
+ activity.dump(pw, "", true);
+ needSeparator = true;
+ }
+ }
+
void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
@@ -6284,6 +6313,8 @@
}
} else if (DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)) {
dumpTopResumedActivityLocked(pw);
+ } else if (DUMP_VISIBLE_ACTIVITIES.equals(cmd)) {
+ dumpVisibleActivitiesLocked(pw);
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index e80c260..fd6c974 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -119,10 +119,20 @@
if (appThread != null) {
callerApp = mService.getProcessController(appThread);
}
- final ActivityStarter starter = mService.getActivityStartController().obtainStarter(
- null /* intent */, "moveToFront");
- if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid,
- callingPackage, -1, -1, callerApp, null, false, null, null)) {
+ final BackgroundActivityStartController balController =
+ mService.getActivityStartController()
+ .getBackgroundActivityLaunchController();
+ if (balController.shouldAbortBackgroundActivityStart(
+ callingUid,
+ callingPid,
+ callingPackage,
+ -1,
+ -1,
+ callerApp,
+ null,
+ false,
+ null,
+ null)) {
if (!mService.isBackgroundActivityStartsEnabled()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
new file mode 100644
index 0000000..d515a27
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -0,0 +1,421 @@
+/*
+ * 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 android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
+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.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Slog;
+
+import com.android.server.am.PendingIntentRecord;
+
+/**
+ * Helper class to check permissions for starting Activities.
+ *
+ * <p>This class collects all the logic to prevent malicious attempts to start activities.
+ */
+public class BackgroundActivityStartController {
+
+ private static final String TAG =
+ TAG_WITH_CLASS_NAME ? "BackgroundActivityStartController" : TAG_ATM;
+
+ private final ActivityTaskManagerService mService;
+ private final ActivityTaskSupervisor mSupervisor;
+
+ BackgroundActivityStartController(
+ final ActivityTaskManagerService service, final ActivityTaskSupervisor supervisor) {
+ mService = service;
+ mSupervisor = supervisor;
+ }
+
+ private boolean isHomeApp(int uid, @Nullable String packageName) {
+ if (mService.mHomeProcess != null) {
+ // Fast check
+ return uid == mService.mHomeProcess.mUid;
+ }
+ if (packageName == null) {
+ return false;
+ }
+ ComponentName activity =
+ mService.getPackageManagerInternalLocked()
+ .getDefaultHomeActivity(UserHandle.getUserId(uid));
+ return activity != null && packageName.equals(activity.getPackageName());
+ }
+
+ boolean shouldAbortBackgroundActivityStart(
+ int callingUid,
+ int callingPid,
+ final String callingPackage,
+ int realCallingUid,
+ int realCallingPid,
+ WindowProcessController callerApp,
+ PendingIntentRecord originatingPendingIntent,
+ boolean allowBackgroundActivityStart,
+ Intent intent,
+ ActivityOptions checkedOptions) {
+ // don't abort for the most important UIDs
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ final boolean useCallingUidState =
+ originatingPendingIntent == null
+ || checkedOptions == null
+ || !checkedOptions.getIgnorePendingIntentCreatorForegroundState();
+ if (useCallingUidState) {
+ if (callingUid == Process.ROOT_UID
+ || callingAppId == Process.SYSTEM_UID
+ || callingAppId == Process.NFC_UID) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Activity start allowed for important callingUid (" + callingUid + ")");
+ }
+ return false;
+ }
+
+ // Always allow home application to start activities.
+ if (isHomeApp(callingUid, callingPackage)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Activity start allowed for home app callingUid (" + callingUid + ")");
+ }
+ return false;
+ }
+
+ // IME should always be allowed to start activity, like IME settings.
+ final WindowState imeWindow =
+ mService.mRootWindowContainer.getCurrentInputMethodWindow();
+ if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");
+ }
+ return false;
+ }
+ }
+
+ // This is used to block background activity launch even if the app is still
+ // visible to user after user clicking home button.
+ final int appSwitchState = mService.getBalAppSwitchesState();
+
+ // don't abort if the callingUid has a visible window or is a persistent system process
+ final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
+ final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
+ final boolean isCallingUidForeground =
+ callingUidHasAnyVisibleWindow
+ || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
+ || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
+ final boolean isCallingUidPersistentSystemProcess =
+ callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+
+ // Normal apps with visible app window will be allowed to start activity if app switching
+ // is allowed, or apps like live wallpaper with non app visible window will be allowed.
+ final boolean appSwitchAllowedOrFg =
+ appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
+ final boolean allowCallingUidStartActivity =
+ ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
+ && callingUidHasAnyVisibleWindow)
+ || isCallingUidPersistentSystemProcess;
+ if (useCallingUidState && allowCallingUidStartActivity) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Activity start allowed: callingUidHasAnyVisibleWindow = "
+ + callingUid
+ + ", isCallingUidPersistentSystemProcess = "
+ + isCallingUidPersistentSystemProcess);
+ }
+ return false;
+ }
+ // take realCallingUid into consideration
+ final int realCallingUidProcState =
+ (callingUid == realCallingUid)
+ ? callingUidProcState
+ : mService.mActiveUids.getUidState(realCallingUid);
+ final boolean realCallingUidHasAnyVisibleWindow =
+ (callingUid == realCallingUid)
+ ? callingUidHasAnyVisibleWindow
+ : mService.hasActiveVisibleWindow(realCallingUid);
+ final boolean isRealCallingUidForeground =
+ (callingUid == realCallingUid)
+ ? isCallingUidForeground
+ : realCallingUidHasAnyVisibleWindow
+ || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
+ final int realCallingAppId = UserHandle.getAppId(realCallingUid);
+ final boolean isRealCallingUidPersistentSystemProcess =
+ (callingUid == realCallingUid)
+ ? isCallingUidPersistentSystemProcess
+ : (realCallingAppId == Process.SYSTEM_UID)
+ || realCallingUidProcState
+ <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+
+ // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
+ // visible window.
+ if (Process.isSdkSandboxUid(realCallingUid)) {
+ int realCallingSdkSandboxUidToAppUid =
+ Process.getAppUidForSdkSandboxUid(UserHandle.getAppId(realCallingUid));
+
+ if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Activity start allowed: uid in SDK sandbox ("
+ + realCallingUid
+ + ") has visible (non-toast) window.");
+ }
+ return false;
+ }
+ }
+
+ // Legacy behavior allows to use caller foreground state to bypass BAL restriction.
+ final boolean balAllowedByPiSender =
+ PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
+
+ if (balAllowedByPiSender && realCallingUid != callingUid) {
+ final boolean useCallerPermission =
+ PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions);
+ if (useCallerPermission
+ && ActivityManager.checkComponentPermission(
+ android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+ realCallingUid,
+ -1,
+ true)
+ == PackageManager.PERMISSION_GRANTED) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Activity start allowed: realCallingUid ("
+ + realCallingUid
+ + ") has BAL permission.");
+ }
+ return false;
+ }
+
+ // don't abort if the realCallingUid has a visible window
+ // TODO(b/171459802): We should check appSwitchAllowed also
+ if (realCallingUidHasAnyVisibleWindow) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Activity start allowed: realCallingUid ("
+ + realCallingUid
+ + ") has visible (non-toast) window");
+ }
+ return false;
+ }
+ // if the realCallingUid is a persistent system process, abort if the IntentSender
+ // wasn't allowed to start an activity
+ if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Activity start allowed: realCallingUid ("
+ + realCallingUid
+ + ") is persistent system process AND intent sender allowed "
+ + "(allowBackgroundActivityStart = true)");
+ }
+ return false;
+ }
+ // don't abort if the realCallingUid is an associated companion app
+ if (mService.isAssociatedCompanionApp(
+ UserHandle.getUserId(realCallingUid), realCallingUid)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Activity start allowed: realCallingUid ("
+ + realCallingUid
+ + ") is companion app");
+ }
+ return false;
+ }
+ }
+ if (useCallingUidState) {
+ // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
+ if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+ == PERMISSION_GRANTED) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND "
+ + "permission granted for uid "
+ + callingUid);
+ }
+ return false;
+ }
+ // don't abort if the caller has the same uid as the recents component
+ if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Background activity start allowed: callingUid ("
+ + callingUid
+ + ") is recents");
+ }
+ return false;
+ }
+ // don't abort if the callingUid is the device owner
+ if (mService.isDeviceOwner(callingUid)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Background activity start allowed: callingUid ("
+ + callingUid
+ + ") is device owner");
+ }
+ return false;
+ }
+ // don't abort if the callingUid has companion device
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Background activity start allowed: callingUid ("
+ + callingUid
+ + ") is companion app");
+ }
+ return false;
+ }
+ // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+ if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+ Slog.w(
+ TAG,
+ "Background activity start for "
+ + callingPackage
+ + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+ return false;
+ }
+ }
+ // If we don't have callerApp at this point, no caller was provided to startActivity().
+ // That's the case for PendingIntent-based starts, since the creator's process might not be
+ // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
+ // caller if caller allows, so that we can make the decision based on its state.
+ int callerAppUid = callingUid;
+ if (callerApp == null && balAllowedByPiSender) {
+ callerApp = mService.getProcessController(realCallingPid, realCallingUid);
+ callerAppUid = realCallingUid;
+ }
+ // don't abort if the callerApp or other processes of that uid are allowed in any way
+ if (callerApp != null && useCallingUidState) {
+ // first check the original calling process
+ if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Background activity start allowed: callerApp process (pid = "
+ + callerApp.getPid()
+ + ", uid = "
+ + callerAppUid
+ + ") is allowed");
+ }
+ return false;
+ }
+ // only if that one wasn't allowed, check the other ones
+ final ArraySet<WindowProcessController> uidProcesses =
+ mService.mProcessMap.getProcesses(callerAppUid);
+ if (uidProcesses != null) {
+ for (int i = uidProcesses.size() - 1; i >= 0; i--) {
+ final WindowProcessController proc = uidProcesses.valueAt(i);
+ if (proc != callerApp
+ && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(
+ TAG,
+ "Background activity start allowed: process "
+ + proc.getPid()
+ + " from uid "
+ + callerAppUid
+ + " is allowed");
+ }
+ return false;
+ }
+ }
+ }
+ }
+ // anything that has fallen through would currently be aborted
+ Slog.w(
+ TAG,
+ "Background activity start [callingPackage: "
+ + callingPackage
+ + "; callingUid: "
+ + callingUid
+ + "; appSwitchState: "
+ + appSwitchState
+ + "; isCallingUidForeground: "
+ + isCallingUidForeground
+ + "; callingUidHasAnyVisibleWindow: "
+ + callingUidHasAnyVisibleWindow
+ + "; callingUidProcState: "
+ + DebugUtils.valueToString(
+ ActivityManager.class, "PROCESS_STATE_", callingUidProcState)
+ + "; isCallingUidPersistentSystemProcess: "
+ + isCallingUidPersistentSystemProcess
+ + "; realCallingUid: "
+ + realCallingUid
+ + "; isRealCallingUidForeground: "
+ + isRealCallingUidForeground
+ + "; realCallingUidHasAnyVisibleWindow: "
+ + realCallingUidHasAnyVisibleWindow
+ + "; realCallingUidProcState: "
+ + DebugUtils.valueToString(
+ ActivityManager.class, "PROCESS_STATE_", realCallingUidProcState)
+ + "; isRealCallingUidPersistentSystemProcess: "
+ + isRealCallingUidPersistentSystemProcess
+ + "; originatingPendingIntent: "
+ + originatingPendingIntent
+ + "; allowBackgroundActivityStart: "
+ + allowBackgroundActivityStart
+ + "; intent: "
+ + intent
+ + "; callerApp: "
+ + callerApp
+ + "; inVisibleTask: "
+ + (callerApp != null && callerApp.hasActivityInVisibleTask())
+ + "]");
+ // log aborted activity start to TRON
+ if (mService.isActivityStartsLoggingEnabled()) {
+ mSupervisor
+ .getActivityMetricsLogger()
+ .logAbortedBgActivityStart(
+ intent,
+ callerApp,
+ callingUid,
+ callingPackage,
+ callingUidProcState,
+ callingUidHasAnyVisibleWindow,
+ realCallingUid,
+ realCallingUidProcState,
+ realCallingUidHasAnyVisibleWindow,
+ (originatingPendingIntent != null));
+ }
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index b033dca..b84b2d8 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -26,6 +26,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.server.wm.DisplayAreaProto.FEATURE_ID;
+import static com.android.server.wm.DisplayAreaProto.IS_IGNORING_ORIENTATION_REQUEST;
import static com.android.server.wm.DisplayAreaProto.IS_ORGANIZED;
import static com.android.server.wm.DisplayAreaProto.IS_ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayAreaProto.IS_TASK_DISPLAY_AREA;
@@ -320,6 +321,7 @@
proto.write(IS_ROOT_DISPLAY_AREA, asRootDisplayArea() != null);
proto.write(FEATURE_ID, mFeatureId);
proto.write(IS_ORGANIZED, isOrganized());
+ proto.write(IS_IGNORING_ORIENTATION_REQUEST, getIgnoreOrientationRequest());
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ac9bcfb..b10e420 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -986,7 +986,9 @@
final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(w);
- if (w.isFocused() && mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
+
+ if (w.getWindowingMode() != WINDOWING_MODE_PINNED
+ && mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
&& preferredModeId != 0) {
mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId;
}
@@ -1082,7 +1084,7 @@
* mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mInsetsStateController = new InsetsStateController(this);
- mDisplayFrames = new DisplayFrames(mDisplayId, mInsetsStateController.getRawInsetsState(),
+ mDisplayFrames = new DisplayFrames(mInsetsStateController.getRawInsetsState(),
mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation),
calculateRoundedCornersForRotation(mDisplayInfo.rotation),
calculatePrivacyIndicatorBoundsForRotation(mDisplayInfo.rotation));
@@ -1938,11 +1940,11 @@
private void startFixedRotationTransform(WindowToken token, int rotation) {
mTmpConfiguration.unset();
final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation);
- final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
+ final DisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
final PrivacyIndicatorBounds indicatorBounds =
calculatePrivacyIndicatorBoundsForRotation(rotation);
- final DisplayFrames displayFrames = new DisplayFrames(mDisplayId, new InsetsState(), info,
+ final DisplayFrames displayFrames = new DisplayFrames(new InsetsState(), info,
cutout, roundedCorners, indicatorBounds);
token.applyFixedRotationTransform(info, displayFrames, mTmpConfiguration);
}
@@ -2149,12 +2151,10 @@
final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
// Update application display metrics.
- final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
- final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
+ final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(rotation);
final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
- final Rect appFrame = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation,
- wmDisplayCutout);
+ final Rect appFrame = mDisplayPolicy.getDecorInsetsInfo(rotation, dw, dh).mNonDecorFrame;
mDisplayInfo.rotation = rotation;
mDisplayInfo.logicalWidth = dw;
mDisplayInfo.logicalHeight = dh;
@@ -2192,9 +2192,10 @@
return mDisplayInfo;
}
- WmDisplayCutout calculateDisplayCutoutForRotation(int rotation) {
+ DisplayCutout calculateDisplayCutoutForRotation(int rotation) {
return mDisplayCutoutCache.getOrCompute(
- mIsSizeForced ? mBaseDisplayCutout : mInitialDisplayCutout, rotation);
+ mIsSizeForced ? mBaseDisplayCutout : mInitialDisplayCutout, rotation)
+ .getDisplayCutout();
}
static WmDisplayCutout calculateDisplayCutoutForRotationAndDisplaySizeUncached(
@@ -2265,9 +2266,7 @@
final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
outConfig.windowConfiguration.setMaxBounds(0, 0, dw, dh);
outConfig.windowConfiguration.setBounds(outConfig.windowConfiguration.getMaxBounds());
-
- final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
- computeScreenAppConfiguration(outConfig, dw, dh, rotation, wmDisplayCutout);
+ computeScreenAppConfiguration(outConfig, dw, dh, rotation);
final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
displayInfo.rotation = rotation;
@@ -2276,7 +2275,7 @@
final Rect appBounds = outConfig.windowConfiguration.getAppBounds();
displayInfo.appWidth = appBounds.width();
displayInfo.appHeight = appBounds.height();
- final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
+ final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(rotation);
displayInfo.displayCutout = displayCutout.isEmpty() ? null : displayCutout;
computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh,
mDisplayMetrics.density, outConfig);
@@ -2285,21 +2284,17 @@
/** Compute configuration related to application without changing current display. */
private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
- int rotation, WmDisplayCutout wmDisplayCutout) {
- final DisplayFrames displayFrames =
- mDisplayPolicy.getSimulatedDisplayFrames(rotation, dw, dh, wmDisplayCutout);
- final Rect appFrame =
- mDisplayPolicy.getNonDecorDisplayFrameWithSimulatedFrame(displayFrames);
+ int rotation) {
+ final DisplayPolicy.DecorInsets.Info info =
+ mDisplayPolicy.getDecorInsetsInfo(rotation, dw, dh);
// AppBounds at the root level should mirror the app screen size.
- outConfig.windowConfiguration.setAppBounds(appFrame);
+ outConfig.windowConfiguration.setAppBounds(info.mNonDecorFrame);
outConfig.windowConfiguration.setRotation(rotation);
outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
final float density = mDisplayMetrics.density;
- final Point configSize =
- mDisplayPolicy.getConfigDisplaySizeWithSimulatedFrame(displayFrames);
- outConfig.screenWidthDp = (int) (configSize.x / density + 0.5f);
- outConfig.screenHeightDp = (int) (configSize.y / density + 0.5f);
+ outConfig.screenWidthDp = (int) (info.mConfigFrame.width() / density + 0.5f);
+ outConfig.screenHeightDp = (int) (info.mConfigFrame.height() / density + 0.5f);
outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);
outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
@@ -2322,8 +2317,7 @@
config.windowConfiguration.setWindowingMode(getWindowingMode());
config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
- computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation,
- calculateDisplayCutoutForRotation(getRotation()));
+ computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation);
config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
| ((displayInfo.flags & Display.FLAG_ROUND) != 0
@@ -2432,9 +2426,8 @@
private int reduceCompatConfigWidthSize(int curSize, int rotation,
DisplayMetrics dm, int dw, int dh) {
- final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
- final Rect nonDecorSize = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation,
- wmDisplayCutout);
+ final Rect nonDecorSize =
+ mDisplayPolicy.getDecorInsetsInfo(rotation, dw, dh).mNonDecorFrame;
dm.noncompatWidthPixels = nonDecorSize.width();
dm.noncompatHeightPixels = nonDecorSize.height();
float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
@@ -2483,11 +2476,8 @@
}
private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh) {
- // Get the display cutout at this rotation.
- final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
-
// Get the app screen size at this rotation.
- final Rect size = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation, wmDisplayCutout);
+ final Rect size = mDisplayPolicy.getDecorInsetsInfo(rotation, dw, dh).mNonDecorFrame;
// Compute the screen layout size class for this rotation.
int longSize = size.width();
@@ -2503,19 +2493,21 @@
}
private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int dw, int dh) {
- final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
- final Point size = mDisplayPolicy.getConfigDisplaySize(dw, dh, rotation, wmDisplayCutout);
- if (size.x < displayInfo.smallestNominalAppWidth) {
- displayInfo.smallestNominalAppWidth = size.x;
+ final DisplayPolicy.DecorInsets.Info info = mDisplayPolicy.getDecorInsetsInfo(
+ rotation, dw, dh);
+ final int w = info.mConfigFrame.width();
+ final int h = info.mConfigFrame.height();
+ if (w < displayInfo.smallestNominalAppWidth) {
+ displayInfo.smallestNominalAppWidth = w;
}
- if (size.x > displayInfo.largestNominalAppWidth) {
- displayInfo.largestNominalAppWidth = size.x;
+ if (w > displayInfo.largestNominalAppWidth) {
+ displayInfo.largestNominalAppWidth = w;
}
- if (size.y < displayInfo.smallestNominalAppHeight) {
- displayInfo.smallestNominalAppHeight = size.y;
+ if (h < displayInfo.smallestNominalAppHeight) {
+ displayInfo.smallestNominalAppHeight = h;
}
- if (size.y > displayInfo.largestNominalAppHeight) {
- displayInfo.largestNominalAppHeight = size.y;
+ if (h > displayInfo.largestNominalAppHeight) {
+ displayInfo.largestNominalAppHeight = h;
}
}
@@ -2786,14 +2778,19 @@
}
private void updateDisplayFrames(boolean notifyInsetsChange) {
- if (mDisplayFrames.update(mDisplayInfo,
- calculateDisplayCutoutForRotation(mDisplayInfo.rotation),
- calculateRoundedCornersForRotation(mDisplayInfo.rotation),
- calculatePrivacyIndicatorBoundsForRotation(mDisplayInfo.rotation))) {
+ if (updateDisplayFrames(mDisplayFrames, mDisplayInfo.rotation,
+ mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
mInsetsStateController.onDisplayFramesUpdated(notifyInsetsChange);
}
}
+ boolean updateDisplayFrames(DisplayFrames displayFrames, int rotation, int w, int h) {
+ return displayFrames.update(rotation, w, h,
+ calculateDisplayCutoutForRotation(rotation),
+ calculateRoundedCornersForRotation(rotation),
+ calculatePrivacyIndicatorBoundsForRotation(rotation));
+ }
+
@Override
void onDisplayChanged(DisplayContent dc) {
super.onDisplayChanged(dc);
@@ -2956,6 +2953,9 @@
+ mBaseDisplayHeight + " on display:" + getDisplayId());
}
}
+ if (mDisplayReady) {
+ mDisplayPolicy.mDecorInsets.invalidate();
+ }
}
/**
@@ -5592,7 +5592,7 @@
outExclusionUnrestricted.setEmpty();
}
final Region unhandled = Region.obtain();
- unhandled.set(0, 0, mDisplayFrames.mDisplayWidth, mDisplayFrames.mDisplayHeight);
+ unhandled.set(0, 0, mDisplayFrames.mWidth, mDisplayFrames.mHeight);
final Rect leftEdge = mInsetsStateController.getSourceProvider(ITYPE_LEFT_GESTURES)
.getSource().getFrame();
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 7ca38b8..33641f7 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -30,8 +30,6 @@
import android.view.PrivacyIndicatorBounds;
import android.view.RoundedCorners;
-import com.android.server.wm.utils.WmDisplayCutout;
-
import java.io.PrintWriter;
/**
@@ -39,8 +37,6 @@
* @hide
*/
public class DisplayFrames {
- public final int mDisplayId;
-
public final InsetsState mInsetsState;
/**
@@ -54,48 +50,45 @@
*/
public final Rect mDisplayCutoutSafe = new Rect();
- public int mDisplayWidth;
- public int mDisplayHeight;
+ public int mWidth;
+ public int mHeight;
public int mRotation;
- public DisplayFrames(int displayId, InsetsState insetsState, DisplayInfo info,
- WmDisplayCutout displayCutout, RoundedCorners roundedCorners,
- PrivacyIndicatorBounds indicatorBounds) {
- mDisplayId = displayId;
+ public DisplayFrames(InsetsState insetsState, DisplayInfo info, DisplayCutout cutout,
+ RoundedCorners roundedCorners, PrivacyIndicatorBounds indicatorBounds) {
mInsetsState = insetsState;
- update(info, displayCutout, roundedCorners, indicatorBounds);
+ update(info.rotation, info.logicalWidth, info.logicalHeight, cutout, roundedCorners,
+ indicatorBounds);
+ }
+
+ DisplayFrames() {
+ mInsetsState = new InsetsState();
}
/**
- * This is called when {@link DisplayInfo} or {@link PrivacyIndicatorBounds} is updated.
+ * This is called if the display info may be changed, e.g. rotation, size, insets.
*
- * @param info the updated {@link DisplayInfo}.
- * @param displayCutout the updated {@link DisplayCutout}.
- * @param roundedCorners the updated {@link RoundedCorners}.
- * @param indicatorBounds the updated {@link PrivacyIndicatorBounds}.
* @return {@code true} if anything has been changed; {@code false} otherwise.
*/
- public boolean update(DisplayInfo info, @NonNull WmDisplayCutout displayCutout,
+ public boolean update(int rotation, int w, int h, @NonNull DisplayCutout displayCutout,
@NonNull RoundedCorners roundedCorners,
@NonNull PrivacyIndicatorBounds indicatorBounds) {
final InsetsState state = mInsetsState;
final Rect safe = mDisplayCutoutSafe;
- final DisplayCutout cutout = displayCutout.getDisplayCutout();
- if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight
- && mRotation == info.rotation
- && state.getDisplayCutout().equals(cutout)
+ if (mRotation == rotation && mWidth == w && mHeight == h
+ && mInsetsState.getDisplayCutout().equals(displayCutout)
&& state.getRoundedCorners().equals(roundedCorners)
&& state.getPrivacyIndicatorBounds().equals(indicatorBounds)) {
return false;
}
- mDisplayWidth = info.logicalWidth;
- mDisplayHeight = info.logicalHeight;
- mRotation = info.rotation;
+ mRotation = rotation;
+ mWidth = w;
+ mHeight = h;
final Rect unrestricted = mUnrestricted;
- unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
+ unrestricted.set(0, 0, w, h);
state.setDisplayFrame(unrestricted);
- state.setDisplayCutout(cutout);
+ state.setDisplayCutout(displayCutout);
state.setRoundedCorners(roundedCorners);
state.setPrivacyIndicatorBounds(indicatorBounds);
state.getDisplayCutoutSafe(safe);
@@ -132,7 +125,6 @@
}
public void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
- + " r=" + mRotation);
+ pw.println(prefix + "DisplayFrames w=" + mWidth + " h=" + mHeight + " r=" + mRotation);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index d4eac8d..537b04d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -19,19 +19,15 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.TYPE_INTERNAL;
-import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -107,7 +103,6 @@
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PixelFormat;
-import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.gui.DropInputMode;
@@ -132,8 +127,6 @@
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.InsetsVisibilities;
-import android.view.PrivacyIndicatorBounds;
-import android.view.RoundedCorners;
import android.view.Surface;
import android.view.View;
import android.view.ViewDebug;
@@ -151,7 +144,6 @@
import com.android.internal.policy.ForceShowNavBarSettingsObserver;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.policy.SystemBarUtils;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.util.ScreenshotHelper;
@@ -166,7 +158,6 @@
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
-import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -242,6 +233,8 @@
private final SystemGesturesPointerEventListener mSystemGestures;
+ final DecorInsets mDecorInsets;
+
private volatile int mLidState = LID_ABSENT;
private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private volatile boolean mHdmiPlugged;
@@ -266,7 +259,6 @@
private WindowState mStatusBar = null;
private volatile WindowState mNotificationShade;
- private final int[] mStatusBarHeightForRotation = new int[4];
private WindowState mNavigationBar = null;
@NavigationBarPosition
private int mNavigationBarPosition = NAV_BAR_BOTTOM;
@@ -353,7 +345,6 @@
private static final Rect sTmpRect = new Rect();
private static final Rect sTmpRect2 = new Rect();
- private static final Rect sTmpLastParentFrame = new Rect();
private static final Rect sTmpDisplayCutoutSafe = new Rect();
private static final ClientWindowFrames sTmpClientFrames = new ClientWindowFrames();
@@ -391,16 +382,6 @@
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
- // TODO (b/235842600): Use public type once we can treat task bar as navigation bar.
- private static final int[] STABLE_TYPES = new int[]{
- ITYPE_TOP_DISPLAY_CUTOUT, ITYPE_RIGHT_DISPLAY_CUTOUT, ITYPE_BOTTOM_DISPLAY_CUTOUT,
- ITYPE_LEFT_DISPLAY_CUTOUT, ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR, ITYPE_CLIMATE_BAR
- };
- private static final int[] NON_DECOR_TYPES = new int[]{
- ITYPE_TOP_DISPLAY_CUTOUT, ITYPE_RIGHT_DISPLAY_CUTOUT, ITYPE_BOTTOM_DISPLAY_CUTOUT,
- ITYPE_LEFT_DISPLAY_CUTOUT, ITYPE_NAVIGATION_BAR
- };
-
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private final WindowManagerInternal.AppTransitionListener mAppTransitionListener;
@@ -444,6 +425,7 @@
: service.mAtmService.mSystemThread
.getSystemUiContext(displayContent.getDisplayId());
mDisplayContent = displayContent;
+ mDecorInsets = new DecorInsets(displayContent);
mLock = service.getWindowManagerLock();
final int displayId = displayContent.getDisplayId();
@@ -1226,7 +1208,7 @@
Math.max(displayFrames.mDisplayCutoutSafe.left, 0);
inOutFrame.left = 0;
inOutFrame.top = 0;
- inOutFrame.bottom = displayFrames.mDisplayHeight;
+ inOutFrame.bottom = displayFrames.mHeight;
inOutFrame.right = leftSafeInset + mLeftGestureInset;
});
mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win,
@@ -1236,8 +1218,8 @@
displayFrames.mUnrestricted.right);
inOutFrame.left = rightSafeInset - mRightGestureInset;
inOutFrame.top = 0;
- inOutFrame.bottom = displayFrames.mDisplayHeight;
- inOutFrame.right = displayFrames.mDisplayWidth;
+ inOutFrame.bottom = displayFrames.mHeight;
+ inOutFrame.right = displayFrames.mWidth;
});
mDisplayContent.setInsetProvider(ITYPE_BOTTOM_TAPPABLE_ELEMENT, win,
(displayFrames, windowContainer, inOutFrame) -> {
@@ -1428,11 +1410,6 @@
return Math.max(statusBarHeight, displayFrames.mDisplayCutoutSafe.top);
}
- @VisibleForTesting
- int getStatusBarHeightForRotation(@Surface.Rotation int rotation) {
- return SystemBarUtils.getStatusBarHeightForRotation(mUiContext, rotation);
- }
-
WindowState getStatusBar() {
return mStatusBar != null ? mStatusBar : mStatusBarAlt;
}
@@ -1928,25 +1905,11 @@
final Resources res = getCurrentUserResources();
final int portraitRotation = displayRotation.getPortraitRotation();
- final int upsideDownRotation = displayRotation.getUpsideDownRotation();
- final int landscapeRotation = displayRotation.getLandscapeRotation();
- final int seascapeRotation = displayRotation.getSeascapeRotation();
if (hasStatusBar()) {
- mStatusBarHeightForRotation[portraitRotation] =
- mStatusBarHeightForRotation[upsideDownRotation] =
- getStatusBarHeightForRotation(portraitRotation);
- mStatusBarHeightForRotation[landscapeRotation] =
- getStatusBarHeightForRotation(landscapeRotation);
- mStatusBarHeightForRotation[seascapeRotation] =
- getStatusBarHeightForRotation(seascapeRotation);
mDisplayCutoutTouchableRegionSize = res.getDimensionPixelSize(
R.dimen.display_cutout_touchable_region_size);
} else {
- mStatusBarHeightForRotation[portraitRotation] =
- mStatusBarHeightForRotation[upsideDownRotation] =
- mStatusBarHeightForRotation[landscapeRotation] =
- mStatusBarHeightForRotation[seascapeRotation] = 0;
mDisplayCutoutTouchableRegionSize = 0;
}
@@ -2052,33 +2015,9 @@
}
/**
- * Return the display frame available after excluding any screen decorations that could never be
- * removed in Honeycomb. That is, system bar or button bar.
- *
- * @return display frame excluding all non-decor insets.
- */
- Rect getNonDecorDisplayFrame(int fullWidth, int fullHeight, int rotation,
- WmDisplayCutout cutout) {
- final DisplayFrames displayFrames =
- getSimulatedDisplayFrames(rotation, fullWidth, fullHeight, cutout);
- return getNonDecorDisplayFrameWithSimulatedFrame(displayFrames);
- }
-
- Rect getNonDecorDisplayFrameWithSimulatedFrame(DisplayFrames displayFrames) {
- final Rect nonDecorInsets =
- getInsetsWithInternalTypes(displayFrames, NON_DECOR_TYPES).toRect();
- final Rect displayFrame = new Rect(displayFrames.mInsetsState.getDisplayFrame());
- displayFrame.inset(nonDecorInsets);
- return displayFrame;
- }
-
- /**
* Get the Navigation Bar Frame height. This dimension is the height of the navigation bar that
* is used for spacing to show additional buttons on the navigation bar (such as the ime
- * switcher when ime is visible) while {@link #getNavigationBarHeight} is used for the visible
- * height that we send to the app as content insets that can be smaller.
- * <p>
- * In car mode it will return the same height as {@link #getNavigationBarHeight}
+ * switcher when ime is visible).
*
* @param rotation specifies rotation to return dimension from
* @return navigation bar frame height
@@ -2091,26 +2030,6 @@
}
/**
- * Return the available screen size that we should report for the
- * configuration. This must be no larger than
- * {@link #getNonDecorDisplayFrame(int, int, int, DisplayCutout)}; it may be smaller
- * than that to account for more transient decoration like a status bar.
- */
- public Point getConfigDisplaySize(int fullWidth, int fullHeight, int rotation,
- WmDisplayCutout wmDisplayCutout) {
- final DisplayFrames displayFrames = getSimulatedDisplayFrames(rotation, fullWidth,
- fullHeight, wmDisplayCutout);
- return getConfigDisplaySizeWithSimulatedFrame(displayFrames);
- }
-
- Point getConfigDisplaySizeWithSimulatedFrame(DisplayFrames displayFrames) {
- final Insets insets = getInsetsWithInternalTypes(displayFrames, STABLE_TYPES);
- Rect configFrame = new Rect(displayFrames.mInsetsState.getDisplayFrame());
- configFrame.inset(insets);
- return new Point(configFrame.width(), configFrame.height());
- }
-
- /**
* Return corner radius in pixels that should be used on windows in order to cover the display.
*
* The radius is only valid for internal displays, since the corner radius of external displays
@@ -2125,89 +2044,150 @@
return mShowingDream;
}
- /**
- * Calculates the stable insets if we already have the non-decor insets.
- *
- * @param inOutInsets The known non-decor insets. It will be modified to stable insets.
- * @param rotation The current display rotation.
- */
- void convertNonDecorInsetsToStableInsets(Rect inOutInsets, int rotation) {
- inOutInsets.top = Math.max(inOutInsets.top, mStatusBarHeightForRotation[rotation]);
+ /** The latest insets and frames for screen configuration calculation. */
+ static class DecorInsets {
+ static class Info {
+ /**
+ * The insets for the areas that could never be removed, i.e. display cutout and
+ * navigation bar. Note that its meaning is actually "decor insets". The "non" is just
+ * because it is used to calculate {@link #mNonDecorFrame}.
+ */
+ final Rect mNonDecorInsets = new Rect();
+
+ /**
+ * The stable insets that can affect configuration. The sources are usually from
+ * display cutout, navigation bar, and status bar.
+ */
+ final Rect mConfigInsets = new Rect();
+
+ /** The display frame available after excluding {@link #mNonDecorInsets}. */
+ final Rect mNonDecorFrame = new Rect();
+
+ /**
+ * The available (stable) screen size that we should report for the configuration.
+ * This must be no larger than {@link #mNonDecorFrame}; it may be smaller than that
+ * to account for more transient decoration like a status bar.
+ */
+ final Rect mConfigFrame = new Rect();
+
+ private boolean mNeedUpdate = true;
+
+ void update(DisplayContent dc, int rotation, int w, int h) {
+ final DisplayFrames df = new DisplayFrames();
+ dc.updateDisplayFrames(df, rotation, w, h);
+ dc.getDisplayPolicy().simulateLayoutDisplay(df);
+ final InsetsState insetsState = df.mInsetsState;
+ final Rect displayFrame = insetsState.getDisplayFrame();
+ final Insets decor = calculateDecorInsetsWithInternalTypes(insetsState);
+ final Insets statusBar = insetsState.calculateInsets(displayFrame,
+ Type.statusBars(), true /* ignoreVisibility */);
+ mNonDecorInsets.set(decor.left, decor.top, decor.right, decor.bottom);
+ mConfigInsets.set(Math.max(statusBar.left, decor.left),
+ Math.max(statusBar.top, decor.top),
+ Math.max(statusBar.right, decor.right),
+ Math.max(statusBar.bottom, decor.bottom));
+ mNonDecorFrame.set(displayFrame);
+ mNonDecorFrame.inset(mNonDecorInsets);
+ mConfigFrame.set(displayFrame);
+ mConfigFrame.inset(mConfigInsets);
+ mNeedUpdate = false;
+ }
+
+ void set(Info other) {
+ mNonDecorInsets.set(other.mNonDecorInsets);
+ mConfigInsets.set(other.mConfigInsets);
+ mNonDecorFrame.set(other.mNonDecorFrame);
+ mConfigFrame.set(other.mConfigFrame);
+ mNeedUpdate = false;
+ }
+
+ @Override
+ public String toString() {
+ return "{nonDecorInsets=" + mNonDecorInsets
+ + ", configInsets=" + mConfigInsets
+ + ", nonDecorFrame=" + mNonDecorFrame
+ + ", configFrame=" + mConfigFrame + '}';
+ }
+ }
+
+ // TODO (b/235842600): Use public type once we can treat task bar as navigation bar.
+ static final int[] INTERNAL_DECOR_TYPES;
+ static {
+ final ArraySet<Integer> decorTypes = InsetsState.toInternalType(
+ Type.displayCutout() | Type.navigationBars());
+ decorTypes.remove(ITYPE_EXTRA_NAVIGATION_BAR);
+ INTERNAL_DECOR_TYPES = new int[decorTypes.size()];
+ for (int i = 0; i < INTERNAL_DECOR_TYPES.length; i++) {
+ INTERNAL_DECOR_TYPES[i] = decorTypes.valueAt(i);
+ }
+ }
+
+ private final DisplayContent mDisplayContent;
+ private final Info[] mInfoForRotation = new Info[4];
+ final Info mTmpInfo = new Info();
+
+ DecorInsets(DisplayContent dc) {
+ mDisplayContent = dc;
+ for (int i = mInfoForRotation.length - 1; i >= 0; i--) {
+ mInfoForRotation[i] = new Info();
+ }
+ }
+
+ Info get(int rotation, int w, int h) {
+ final Info info = mInfoForRotation[rotation];
+ if (info.mNeedUpdate) {
+ info.update(mDisplayContent, rotation, w, h);
+ }
+ return info;
+ }
+
+ /** Called when the screen decor insets providers have changed. */
+ void invalidate() {
+ for (Info info : mInfoForRotation) {
+ info.mNeedUpdate = true;
+ }
+ }
+
+ // TODO (b/235842600): Remove this method once we can treat task bar as navigation bar.
+ private static Insets calculateDecorInsetsWithInternalTypes(InsetsState state) {
+ final Rect frame = state.getDisplayFrame();
+ Insets insets = Insets.NONE;
+ for (int i = INTERNAL_DECOR_TYPES.length - 1; i >= 0; i--) {
+ final InsetsSource source = state.peekSource(INTERNAL_DECOR_TYPES[i]);
+ if (source != null) {
+ insets = Insets.max(source.calculateInsets(frame, true /* ignoreVisibility */),
+ insets);
+ }
+ }
+ return insets;
+ }
}
/**
- * Calculates the stable insets without running a layout.
- *
- * @param displayRotation the current display rotation
- * @param displayWidth full display width
- * @param displayHeight full display height
- * @param displayCutout the current display cutout
- * @param outInsets the insets to return
+ * If the decor insets changes, the display configuration may be affected. The caller should
+ * call {@link DisplayContent#sendNewConfiguration()} if this method returns {@code true}.
*/
- public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- WmDisplayCutout displayCutout, Rect outInsets) {
- final DisplayFrames displayFrames = getSimulatedDisplayFrames(displayRotation,
- displayWidth, displayHeight, displayCutout);
- getStableInsetsWithSimulatedFrame(displayFrames, outInsets);
+ boolean updateDecorInsetsInfoIfNeeded(WindowState win) {
+ if (!win.providesNonDecorInsets()) {
+ return false;
+ }
+ final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames;
+ final int rotation = displayFrames.mRotation;
+ final int dw = displayFrames.mWidth;
+ final int dh = displayFrames.mHeight;
+ final DecorInsets.Info newInfo = mDecorInsets.mTmpInfo;
+ newInfo.update(mDisplayContent, rotation, dw, dh);
+ final DecorInsets.Info currentInfo = getDecorInsetsInfo(rotation, dw, dh);
+ if (newInfo.mNonDecorFrame.equals(currentInfo.mNonDecorFrame)) {
+ return false;
+ }
+ mDecorInsets.invalidate();
+ mDecorInsets.mInfoForRotation[rotation].set(newInfo);
+ return true;
}
- void getStableInsetsWithSimulatedFrame(DisplayFrames displayFrames, Rect outInsets) {
- // Navigation bar, status bar, and cutout.
- outInsets.set(getInsetsWithInternalTypes(displayFrames, STABLE_TYPES).toRect());
- }
-
- /**
- * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
- * bar or button bar. See {@link #getNonDecorDisplayFrame}.
- *
- * @param displayRotation the current display rotation
- * @param fullWidth the width of the display, including all insets
- * @param fullHeight the height of the display, including all insets
- * @param cutout the current display cutout
- * @param outInsets the insets to return
- */
- public void getNonDecorInsetsLw(int displayRotation, int fullWidth, int fullHeight,
- WmDisplayCutout cutout, Rect outInsets) {
- final DisplayFrames displayFrames =
- getSimulatedDisplayFrames(displayRotation, fullWidth, fullHeight, cutout);
- getNonDecorInsetsWithSimulatedFrame(displayFrames, outInsets);
- }
-
- void getNonDecorInsetsWithSimulatedFrame(DisplayFrames displayFrames, Rect outInsets) {
- outInsets.set(getInsetsWithInternalTypes(displayFrames, NON_DECOR_TYPES).toRect());
- }
-
- DisplayFrames getSimulatedDisplayFrames(int displayRotation, int fullWidth,
- int fullHeight, WmDisplayCutout cutout) {
- final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
- info.rotation = displayRotation;
- info.logicalWidth = fullWidth;
- info.logicalHeight = fullHeight;
- info.displayCutout = cutout.getDisplayCutout();
- final RoundedCorners roundedCorners =
- mDisplayContent.calculateRoundedCornersForRotation(displayRotation);
- final PrivacyIndicatorBounds indicatorBounds =
- mDisplayContent.calculatePrivacyIndicatorBoundsForRotation(displayRotation);
- final DisplayFrames displayFrames = new DisplayFrames(getDisplayId(), new InsetsState(),
- info, cutout, roundedCorners, indicatorBounds);
- simulateLayoutDisplay(displayFrames);
- return displayFrames;
- }
-
- @VisibleForTesting
- Insets getInsets(DisplayFrames displayFrames, @InsetsType int type) {
- final InsetsState state = displayFrames.mInsetsState;
- final Insets insets = state.calculateInsets(state.getDisplayFrame(), type,
- true /* ignoreVisibility */);
- return insets;
- }
-
- Insets getInsetsWithInternalTypes(DisplayFrames displayFrames,
- @InternalInsetsType int[] types) {
- final InsetsState state = displayFrames.mInsetsState;
- final Insets insets = state.calculateInsetsWithInternalTypes(state.getDisplayFrame(), types,
- true /* ignoreVisibility */);
- return insets;
+ DecorInsets.Info getDecorInsetsInfo(int rotation, int w, int h) {
+ return mDecorInsets.get(rotation, w, h);
}
@NavigationBarPosition
@@ -2850,6 +2830,11 @@
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars=");
pw.println(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
+ pw.print(prefix); pw.println("mDecorInsetsInfo:");
+ for (int rotation = 0; rotation < mDecorInsets.mInfoForRotation.length; rotation++) {
+ final DecorInsets.Info info = mDecorInsets.mInfoForRotation[rotation];
+ pw.println(prefixInner + Surface.rotationToString(rotation) + "=" + info);
+ }
mSystemGestures.dump(pw, prefix);
pw.print(prefix); pw.println("Looper state:");
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index ed771c2..ac1fbc3 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -34,6 +34,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
+import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
@@ -44,6 +45,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Manages global window inset state in the system represented by {@link InsetsState}.
@@ -86,8 +88,17 @@
}
};
+ private final Function<Integer, WindowContainerInsetsSourceProvider> mSourceProviderFunc;
+
InsetsStateController(DisplayContent displayContent) {
mDisplayContent = displayContent;
+ mSourceProviderFunc = type -> {
+ final InsetsSource source = mState.getSource(type);
+ if (type == ITYPE_IME) {
+ return new ImeInsetsSourceProvider(source, this, mDisplayContent);
+ }
+ return new WindowContainerInsetsSourceProvider(source, this, mDisplayContent);
+ };
}
InsetsState getRawInsetsState() {
@@ -115,15 +126,7 @@
* @return The provider of a specific type.
*/
WindowContainerInsetsSourceProvider getSourceProvider(@InternalInsetsType int type) {
- if (type == ITYPE_IME) {
- return mProviders.computeIfAbsent(type,
- key -> new ImeInsetsSourceProvider(
- mState.getSource(key), this, mDisplayContent));
- } else {
- return mProviders.computeIfAbsent(type,
- key -> new WindowContainerInsetsSourceProvider(mState.getSource(key), this,
- mDisplayContent));
- }
+ return mProviders.computeIfAbsent(type, mSourceProviderFunc);
}
ImeInsetsSourceProvider getImeSourceProvider() {
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 6602d29..4f506a5 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -14,3 +14,6 @@
tigerhuang@google.com
lihongyu@google.com
mariiasand@google.com
+
+per-file BackgroundActivityStartController.java = set noparent
+per-file BackgroundActivityStartController.java = brufino@google.com, ogunwale@google.com, louischang@google.com, lus@google.com, rickywai@google.com
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 659e37b..a9d5c69 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1402,13 +1402,15 @@
/**
* Reorder the history task so that the passed activity is brought to the front.
+ * @return whether it was actually moved (vs already being top).
*/
- final void moveActivityToFrontLocked(ActivityRecord newTop) {
+ final boolean moveActivityToFrontLocked(ActivityRecord newTop) {
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing and adding activity %s to root task at top "
+ "callers=%s", newTop, Debug.getCallers(4));
-
+ int origDist = getDistanceFromTop(newTop);
positionChildAtTop(newTop);
updateEffectiveIntent();
+ return getDistanceFromTop(newTop) != origDist;
}
@Override
@@ -1607,14 +1609,14 @@
}
}
- ActivityRecord performClearTop(ActivityRecord newR, int launchFlags) {
+ ActivityRecord performClearTop(ActivityRecord newR, int launchFlags, int[] finishCount) {
// The task should be preserved for putting new activity in case the last activity is
// finished if it is normal launch mode and not single top ("clear-task-top").
mReuseTask = true;
mTaskSupervisor.beginDeferResume();
final ActivityRecord result;
try {
- result = clearTopActivities(newR, launchFlags);
+ result = clearTopActivities(newR, launchFlags, finishCount);
} finally {
mTaskSupervisor.endDeferResume();
mReuseTask = false;
@@ -1630,14 +1632,19 @@
* activities on top of it and return the instance.
*
* @param newR Description of the new activity being started.
+ * @param finishCount 1-element array that will be populated with the number of activities
+ * that have been finished.
* @return Returns the existing activity in the task that performs the clear-top operation,
* or {@code null} if none was found.
*/
- private ActivityRecord clearTopActivities(ActivityRecord newR, int launchFlags) {
- final ActivityRecord r = findActivityInHistory(newR.mActivityComponent);
+ private ActivityRecord clearTopActivities(ActivityRecord newR, int launchFlags,
+ int[] finishCount) {
+ final ActivityRecord r = findActivityInHistory(newR.mActivityComponent, newR.mUserId);
if (r == null) return null;
- final PooledPredicate f = PooledLambda.obtainPredicate(Task::finishActivityAbove,
+ final PooledPredicate f = PooledLambda.obtainPredicate(
+ (ActivityRecord ar, ActivityRecord boundaryActivity) ->
+ finishActivityAbove(ar, boundaryActivity, finishCount),
PooledLambda.__(ActivityRecord.class), r);
forAllActivities(f);
f.recycle();
@@ -1655,7 +1662,8 @@
return r;
}
- private static boolean finishActivityAbove(ActivityRecord r, ActivityRecord boundaryActivity) {
+ private static boolean finishActivityAbove(ActivityRecord r, ActivityRecord boundaryActivity,
+ @NonNull int[] finishCount) {
// Stop operation once we reach the boundary activity.
if (r == boundaryActivity) return true;
@@ -1666,6 +1674,7 @@
// TODO: Why is this updating the boundary activity vs. the current activity???
boundaryActivity.updateOptionsLocked(opts);
}
+ finishCount[0] += 1;
r.finishIfPossible("clear-task-stack", false /* oomAdj */);
}
@@ -1750,17 +1759,18 @@
* Find the activity in the history task within the given task. Returns
* the index within the history at which it's found, or < 0 if not found.
*/
- ActivityRecord findActivityInHistory(ComponentName component) {
+ ActivityRecord findActivityInHistory(ComponentName component, int userId) {
final PooledPredicate p = PooledLambda.obtainPredicate(Task::matchesActivityInHistory,
- PooledLambda.__(ActivityRecord.class), component);
+ PooledLambda.__(ActivityRecord.class), component, userId);
final ActivityRecord r = getActivity(p);
p.recycle();
return r;
}
private static boolean matchesActivityInHistory(
- ActivityRecord r, ComponentName activityComponent) {
- return !r.finishing && r.mActivityComponent.equals(activityComponent);
+ ActivityRecord r, ComponentName activityComponent, int userId) {
+ return !r.finishing && r.mActivityComponent.equals(activityComponent)
+ && r.mUserId == userId;
}
/** Updates the last task description values. */
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 8872b35..1f6690ac 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -98,7 +98,6 @@
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.am.HostingRecord;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.wm.utils.WmDisplayCutout;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -298,10 +297,11 @@
final Point mLastSurfaceSize = new Point();
- private final Rect mTmpInsets = new Rect();
private final Rect mTmpBounds = new Rect();
private final Rect mTmpFullBounds = new Rect();
+ /** For calculating screenWidthDp and screenWidthDp, i.e. the area without the system bars. */
private final Rect mTmpStableBounds = new Rect();
+ /** For calculating app bounds, i.e. the area without the nav bar and display cutout. */
private final Rect mTmpNonDecorBounds = new Rect();
//TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
@@ -2202,21 +2202,16 @@
DisplayInfo displayInfo) {
outNonDecorBounds.set(bounds);
outStableBounds.set(bounds);
- final Task rootTask = getRootTaskFragment().asTask();
- if (rootTask == null || rootTask.mDisplayContent == null) {
+ if (mDisplayContent == null) {
return;
}
mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
- final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- final WmDisplayCutout cutout =
- rootTask.mDisplayContent.calculateDisplayCutoutForRotation(displayInfo.rotation);
- final DisplayFrames displayFrames = policy.getSimulatedDisplayFrames(displayInfo.rotation,
- displayInfo.logicalWidth, displayInfo.logicalHeight, cutout);
- policy.getNonDecorInsetsWithSimulatedFrame(displayFrames, mTmpInsets);
- intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
- policy.getStableInsetsWithSimulatedFrame(displayFrames, mTmpInsets);
- intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ final DisplayPolicy.DecorInsets.Info info = policy.getDecorInsetsInfo(
+ displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, info.mNonDecorInsets);
+ intersectWithInsetsIfFits(outStableBounds, mTmpBounds, info.mConfigInsets);
}
/**
@@ -2280,19 +2275,33 @@
super.onConfigurationChanged(newParentConfig);
- if (shouldStartChangeTransition(mTmpPrevBounds)) {
+ final boolean shouldStartChangeTransition = shouldStartChangeTransition(mTmpPrevBounds);
+ if (shouldStartChangeTransition) {
initializeChangeTransition(mTmpPrevBounds);
- } else if (mTaskFragmentOrganizer != null) {
- // Update the surface here instead of in the organizer so that we can make sure
- // it can be synced with the surface freezer.
- final SurfaceControl.Transaction t = getSyncTransaction();
- updateSurfacePosition(t);
- updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
+ }
+ if (mTaskFragmentOrganizer != null) {
+ if (mTransitionController.isShellTransitionsEnabled()
+ && !mTransitionController.isCollecting(this)) {
+ // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
+ // update the surface here if it is not collected by Shell transition.
+ updateOrganizedTaskFragmentSurface();
+ } else if (!mTransitionController.isShellTransitionsEnabled()
+ && !shouldStartChangeTransition) {
+ // Update the surface here instead of in the organizer so that we can make sure
+ // it can be synced with the surface freezer for legacy app transition.
+ updateOrganizedTaskFragmentSurface();
+ }
}
sendTaskFragmentInfoChanged();
}
+ private void updateOrganizedTaskFragmentSurface() {
+ final SurfaceControl.Transaction t = getSyncTransaction();
+ updateSurfacePosition(t);
+ updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
+ }
+
/** Updates the surface size so that the sub windows cannot be shown out of bounds. */
private void updateOrganizedTaskFragmentSurfaceSize(SurfaceControl.Transaction t,
boolean forceUpdate) {
@@ -2347,33 +2356,16 @@
|| endBounds.height() != startBounds.height();
}
- boolean canHaveEmbeddingActivityTransition(@NonNull ActivityRecord child) {
- if (!isOrganizedTaskFragment() || !mTransitionController.isShellTransitionsEnabled()) {
- return false;
- }
- // The activity should request open transition when it is becoming visible.
- return child.isVisibleRequested();
- }
-
- void collectEmbeddedTaskFragmentIfNeeded() {
- if (!isOrganizedTaskFragment() || mTransitionController.isCollecting(this)) {
- return;
- }
- if (getChildCount() == 0) {
- // The TaskFragment is new created, and just becoming non-empty.
- mTransitionController.collectExistenceChange(this);
- } else {
- mTransitionController.collect(this);
- }
+ @Override
+ boolean isSyncFinished() {
+ return super.isSyncFinished() && isReadyToTransit();
}
@Override
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);
if (mTaskFragmentOrganizer != null) {
- final SurfaceControl.Transaction t = getSyncTransaction();
- updateSurfacePosition(t);
- updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
+ updateOrganizedTaskFragmentSurface();
// If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to
// emit the callbacks now.
sendTaskFragmentAppeared();
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index c7e39ce..16fe4da 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -73,6 +73,7 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.RemoteTransition;
@@ -84,11 +85,13 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.wm.utils.RotationAnimationUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -280,9 +283,9 @@
if (mContainerFreezer == null) {
mContainerFreezer = new ScreenshotFreezer();
}
- mIsSeamlessRotation = true;
final WindowState top = dc.getDisplayPolicy().getTopFullscreenOpaqueWindow();
if (top != null) {
+ mIsSeamlessRotation = true;
top.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Override sync-method for %s "
+ "because seamless rotating", top.getName());
@@ -698,7 +701,11 @@
// remove the surfaces yet. If it is currently visible, but not expected-visible,
// then doing commitVisibility here would actually be out-of-order and leave the
// activity in a bad state.
- if (!visibleAtTransitionEnd && !ar.isVisibleRequested()) {
+ // TODO (b/243755838) Create a screen off transition to correct the visible status
+ // of activities.
+ final boolean isScreenOff = ar.mDisplayContent == null
+ || ar.mDisplayContent.getDisplayInfo().state == Display.STATE_OFF;
+ if ((!visibleAtTransitionEnd || isScreenOff) && !ar.isVisibleRequested()) {
final boolean commitVisibility = !checkEnterPipOnFinish(ar);
// Avoid commit visibility if entering pip or else we will get a sudden
// "flash" / surface going invisible for a split second.
@@ -720,6 +727,10 @@
if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) {
// Legacy dispatch relies on this (for now).
ar.mEnteringAnimation = visibleAtTransitionEnd;
+ } else if (mTransientLaunches != null && mTransientLaunches.containsKey(ar)
+ && ar.isVisible()) {
+ // Transient launch was committed, so report enteringAnimation
+ ar.mEnteringAnimation = true;
}
continue;
}
@@ -1595,6 +1606,9 @@
change.setEndAbsBounds(bounds);
}
change.setRotation(info.mRotation, endRotation);
+ if (info.mSnapshot != null) {
+ change.setSnapshot(info.mSnapshot, info.mSnapshotLuma);
+ }
out.addChange(change);
}
@@ -1750,6 +1764,10 @@
/** These are just extra info. They aren't used for change-detection. */
@Flag int mFlags = FLAG_NONE;
+ /** Snapshot surface and luma, if relevant. */
+ SurfaceControl mSnapshot;
+ float mSnapshotLuma;
+
ChangeInfo(@NonNull WindowContainer origState) {
mVisible = origState.isVisibleRequested();
mWindowingMode = origState.getWindowingMode();
@@ -2058,8 +2076,8 @@
*/
@VisibleForTesting
private class ScreenshotFreezer implements IContainerFreezer {
- /** Values are the screenshot "surfaces" or null if it was frozen via BLAST override. */
- private final ArrayMap<WindowContainer, SurfaceControl> mSnapshots = new ArrayMap<>();
+ /** Keeps track of which windows are frozen. Not all frozen windows have snapshots. */
+ private final ArraySet<WindowContainer> mFrozen = new ArraySet<>();
/** Takes a screenshot and puts it at the top of the container's surface. */
@Override
@@ -2069,7 +2087,7 @@
// Check if any parents have already been "frozen". If so, `wc` is already part of that
// snapshot, so just skip it.
for (WindowContainer p = wc; p != null; p = p.getParent()) {
- if (mSnapshots.containsKey(p)) return false;
+ if (mFrozen.contains(p)) return false;
}
if (mIsSeamlessRotation) {
@@ -2078,7 +2096,7 @@
if (top != null && (top == wc || top.isDescendantOf(wc))) {
// Don't use screenshots for seamless windows: these will use BLAST even if not
// BLAST mode.
- mSnapshots.put(wc, null);
+ mFrozen.add(wc);
return true;
}
}
@@ -2111,7 +2129,15 @@
.setCallsite("Transition.ScreenshotSync")
.setBLASTLayer()
.build();
- mSnapshots.put(wc, snapshotSurface);
+ mFrozen.add(wc);
+ final ChangeInfo changeInfo = Objects.requireNonNull(mChanges.get(wc));
+ changeInfo.mSnapshot = snapshotSurface;
+ if (wc.asDisplayContent() != null) {
+ // This isn't cheap, so only do it for rotations: assume display-level is rotate
+ // since most of the time it is.
+ changeInfo.mSnapshotLuma = RotationAnimationUtils.getMedianBorderLuma(
+ screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace());
+ }
SurfaceControl.Transaction t = wc.mWmService.mTransactionFactory.get();
t.setBuffer(snapshotSurface, buffer);
@@ -2132,11 +2158,12 @@
@Override
public void cleanUp(SurfaceControl.Transaction t) {
- for (int i = 0; i < mSnapshots.size(); ++i) {
- SurfaceControl snap = mSnapshots.valueAt(i);
+ for (int i = 0; i < mFrozen.size(); ++i) {
+ SurfaceControl snap =
+ Objects.requireNonNull(mChanges.get(mFrozen.valueAt(i))).mSnapshot;
// May be null if it was frozen via BLAST override.
if (snap == null) continue;
- t.remove(snap);
+ t.reparent(snap, null /* newParent */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 8269d96..221e186 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -227,6 +227,14 @@
}
/**
+ * @return the collecting transition. {@code null} if there is no collecting transition.
+ */
+ @Nullable
+ Transition getCollectingTransition() {
+ return mCollectingTransition;
+ }
+
+ /**
* @return the collecting transition sync Id. This should only be called when there is a
* collecting transition.
*/
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d820ec4..bc66852 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1837,6 +1837,11 @@
return null;
}
+ int getDistanceFromTop(WindowContainer child) {
+ int idx = mChildren.indexOf(child);
+ return idx < 0 ? -1 : mChildren.size() - 1 - idx;
+ }
+
private ActivityRecord processGetActivityWithBoundary(Predicate<ActivityRecord> callback,
WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
boolean[] boundaryFound, WindowContainer wc) {
@@ -2831,7 +2836,6 @@
void initializeChangeTransition(Rect startBounds, @Nullable SurfaceControl freezeTarget) {
if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
mDisplayContent.mTransitionController.collectVisibleChange(this);
- // TODO(b/207070762): request shell transition for activityEmbedding change.
return;
}
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 236d0dd..5ad6fbd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -318,7 +318,6 @@
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
-import com.android.server.wm.utils.WmDisplayCutout;
import dalvik.annotation.optimization.NeverCompile;
@@ -1834,7 +1833,7 @@
+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
if ((win.isVisibleRequestedOrAdding() && displayContent.updateOrientation())
- || win.providesNonDecorInsets()) {
+ || displayPolicy.updateDecorInsetsInfoIfNeeded(win)) {
displayContent.sendNewConfiguration();
}
@@ -2230,7 +2229,7 @@
Arrays.fill(outActiveControls, null);
}
int result = 0;
- boolean configChanged;
+ boolean configChanged = false;
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
@@ -2297,10 +2296,15 @@
flagChanges = win.mAttrs.flags ^ attrs.flags;
privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
attrChanges = win.mAttrs.copyFrom(attrs);
- if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
- | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
+ final boolean layoutChanged =
+ (attrChanges & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0;
+ if (layoutChanged || (attrChanges
+ & WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
win.mLayoutNeeded = true;
}
+ if (layoutChanged) {
+ configChanged = displayPolicy.updateDecorInsetsInfoIfNeeded(win);
+ }
if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
win.mActivityRecord.checkKeyguardFlagsChanged();
@@ -2504,7 +2508,7 @@
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation");
- configChanged = displayContent.updateOrientation();
+ configChanged |= displayContent.updateOrientation();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (toBeDisplayed && win.mIsWallpaper) {
@@ -2542,7 +2546,7 @@
if (outSyncIdBundle != null) {
final int maybeSyncSeqId;
- if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility != View.GONE
+ if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility == View.VISIBLE
&& win.mSyncSeqId > lastSyncSeqId) {
maybeSyncSeqId = win.shouldSyncWithBuffers() ? win.mSyncSeqId : -1;
win.markRedrawForSyncReported();
@@ -7151,9 +7155,8 @@
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc != null) {
final DisplayInfo di = dc.getDisplayInfo();
- final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(di.rotation);
- dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- cutout, outInsets);
+ outInsets.set(dc.getDisplayPolicy().getDecorInsetsInfo(
+ di.rotation, di.logicalWidth, di.logicalHeight).mConfigInsets);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index ff43a96..c22091b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -34,7 +34,6 @@
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -47,8 +46,6 @@
import com.android.internal.os.ByteTransferPipe;
import com.android.internal.protolog.ProtoLogImpl;
-import com.android.server.LocalServices;
-import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import com.android.server.wm.LetterboxConfiguration.LetterboxHorizontalReachabilityPosition;
import com.android.server.wm.LetterboxConfiguration.LetterboxVerticalReachabilityPosition;
@@ -56,7 +53,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
@@ -106,19 +102,11 @@
// trace files can be written.
return mInternal.mWindowTracing.onShellCommand(this);
case "logging":
- String[] args = peekRemainingArgs();
int result = ProtoLogImpl.getSingleInstance().onShellCommand(this);
if (result != 0) {
- // Let the shell try and handle this
- try (ParcelFileDescriptor pfd
- = ParcelFileDescriptor.dup(getOutFileDescriptor())){
- pw.println("Not handled, calling status bar with args: "
- + Arrays.toString(args));
- LocalServices.getService(StatusBarManagerInternal.class)
- .handleWindowManagerLoggingCommand(args, pfd);
- } catch (IOException e) {
- pw.println("Failed to handle logging command: " + e.getMessage());
- }
+ pw.println("Not handled, please use "
+ + "`adb shell dumpsys activity service SystemUIService WMShell` "
+ + "if you are looking for ProtoLog in WMShell");
}
return result;
case "user-rotation":
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d34ad7d..9456f0f 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -21,7 +21,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -416,7 +415,10 @@
if (!shouldApplyIndependently) {
// Although there is an active sync, we want to apply the transaction now.
- if (!mTransitionController.isCollecting()) {
+ // TODO(b/232042367) Redesign the organizer update on activity callback so that we
+ // we will know about the transition explicitly.
+ final Transition transition = mTransitionController.getCollectingTransition();
+ if (transition == null) {
// This should rarely happen, and we should try to avoid using
// {@link #applySyncTransaction} with Shell transition.
// We still want to apply and merge the transaction to the active sync
@@ -426,8 +428,7 @@
+ " because there is an ongoing sync for"
+ " applySyncTransaction().");
}
- // TODO(b/207070762) make sure changes are all collected.
- applyTransaction(wct, -1 /* syncId */, null /* transition */, caller);
+ applyTransaction(wct, -1 /* syncId */, transition, caller);
return;
}
@@ -821,7 +822,8 @@
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
final TaskFragmentCreationParams taskFragmentCreationOptions =
hop.getTaskFragmentCreationOptions();
- createTaskFragment(taskFragmentCreationOptions, errorCallbackToken, caller);
+ createTaskFragment(taskFragmentCreationOptions, errorCallbackToken, caller,
+ transition);
break;
}
case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
@@ -848,7 +850,8 @@
break;
}
}
- effects |= deleteTaskFragment(taskFragment, organizer, errorCallbackToken);
+ effects |= deleteTaskFragment(taskFragment, organizer, errorCallbackToken,
+ transition);
break;
}
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
@@ -922,8 +925,15 @@
break;
}
- prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(parent,
- activity);
+ if (transition != null) {
+ transition.collect(activity);
+ if (activity.getParent() != null) {
+ // Collect the current parent. Its visibility may change as a result of
+ // this reparenting.
+ transition.collect(activity.getParent());
+ }
+ transition.collect(parent);
+ }
activity.reparent(parent, POSITION_TOP);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
@@ -1118,7 +1128,7 @@
break;
}
reparentTaskFragment(oldParent.asTaskFragment(), newParent, organizer,
- errorCallbackToken);
+ errorCallbackToken, transition);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
}
@@ -1162,41 +1172,6 @@
return effects;
}
- private void prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(
- @NonNull TaskFragment taskFragment, @NonNull ActivityRecord activity) {
- if (!taskFragment.canHaveEmbeddingActivityTransition(activity)) {
- return;
- }
-
- // The reparent can happen in the following cases:
- // 1. Reparent an existing activity to split when app launches new intent.
- // - This happens after app calls to start activity, but before the activity is actually
- // started, so we don't expect any collecting transition, but if it does, we can't
- // queue the WCT because the start activity won't wait.
- // 2. Reparent an existing activity to split to launch placeholder when Task size changed.
- // - We expect to have a collecting transition for the Task resize, so just collect.
- // 3. Reparent a new launching activity to an always-expand container.
- // 4. Reparent a new launching activity to split to launch placeholder together.
- // 5. Reparent a new launching activity to an existing split.
- // - The new launching activity should have start an OPEN transition, so just collect.
- // 6. Reparent PiP activity back to the original Task.
- // - This should be part of the exiting PiP transition, so just collect.
-
- if (!taskFragment.getBounds().equals(activity.getBounds()) && activity.isVisible()
- && !mTransitionController.isCollecting()) {
- // 1. Reparent an existing activity to split when app launches new intent.
- mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE, activity);
- }
-
- // We expect the activity to be in the transition already, so just collect the TaskFragment.
- if (mTransitionController.isCollecting(activity)) {
- taskFragment.collectEmbeddedTaskFragmentIfNeeded();
- } else {
- ProtoLog.w(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Reparenting Activity"
- + " to embedded TaskFragment, but the Activity is not collected");
- }
- }
-
/** A helper method to send minimum dimension violation error to the client. */
private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
IBinder errorCallbackToken, String reason) {
@@ -1773,8 +1748,9 @@
}
}
- void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
- @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
+ private void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
+ @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller,
+ @Nullable Transition transition) {
final ActivityRecord ownerActivity =
ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface(
@@ -1829,11 +1805,13 @@
taskFragment.setWindowingMode(creationParams.getWindowingMode());
taskFragment.setBounds(creationParams.getInitialBounds());
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
+
+ if (transition != null) transition.collectExistenceChange(taskFragment);
}
- void reparentTaskFragment(@NonNull TaskFragment oldParent,
+ private void reparentTaskFragment(@NonNull TaskFragment oldParent,
@Nullable WindowContainer<?> newParent, @Nullable ITaskFragmentOrganizer organizer,
- @Nullable IBinder errorCallbackToken) {
+ @Nullable IBinder errorCallbackToken, @Nullable Transition transition) {
final TaskFragment newParentTF;
if (newParent == null) {
// Use the old parent's parent if the caller doesn't specify the new parent.
@@ -1875,13 +1853,24 @@
HIERARCHY_OP_TYPE_REPARENT_CHILDREN, exception);
return;
}
+ if (transition != null) {
+ // Collect the current parent. It's visibility may change as a result of this
+ // reparenting.
+ transition.collect(oldParent);
+ transition.collect(newParentTF);
+ }
while (oldParent.hasChild()) {
- oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
+ final WindowContainer child = oldParent.getChildAt(0);
+ if (transition != null) {
+ transition.collect(child);
+ }
+ child.reparent(newParentTF, POSITION_TOP);
}
}
private int deleteTaskFragment(@NonNull TaskFragment taskFragment,
- @Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken) {
+ @Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken,
+ @Nullable Transition transition) {
final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
if (index < 0) {
final Throwable exception =
@@ -1901,6 +1890,9 @@
HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT, exception);
return 0;
}
+
+ if (transition != null) transition.collectExistenceChange(taskFragment);
+
mLaunchTaskFragments.removeAt(index);
taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
return TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2e7e78d..df7b8bb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2510,6 +2510,7 @@
mWinAnimator.mSurfaceController,
Debug.getCallers(5));
+ final DisplayContent displayContent = getDisplayContent();
final long origId = Binder.clearCallingIdentity();
try {
@@ -2564,7 +2565,7 @@
// Set up a replacement input channel since the app is now dead.
// We need to catch tapping on the dead window to restart the app.
openInputChannel(null);
- getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/);
+ displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
return;
}
@@ -2572,7 +2573,7 @@
// usually unnoticeable (e.g. covered by rotation animation) and the animation
// bounds could be inconsistent, such as depending on when the window applies
// its draw transaction with new rotation.
- final boolean allowExitAnimation = !getDisplayContent().inTransition()
+ final boolean allowExitAnimation = !displayContent.inTransition()
// There will be a new window so the exit animation may not be visible or
// look weird if its orientation is changed.
&& !inRelaunchingActivity();
@@ -2622,18 +2623,11 @@
}
removeImmediately();
- boolean sentNewConfig = false;
- if (wasVisible) {
- // Removing a visible window will effect the computed orientation
- // So just update orientation if needed.
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent.updateOrientation()) {
- displayContent.sendNewConfiguration();
- sentNewConfig = true;
- }
- }
- if (!sentNewConfig && providesNonDecorInsets()) {
- getDisplayContent().sendNewConfiguration();
+ // Removing a visible window may affect the display orientation so just update it if
+ // needed. Also recompute configuration if it provides screen decor insets.
+ if ((wasVisible && displayContent.updateOrientation())
+ || displayContent.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(this)) {
+ displayContent.sendNewConfiguration();
}
mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
@@ -5953,10 +5947,10 @@
@Override
boolean isSyncFinished() {
- if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mViewVisibility == View.GONE
+ if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mViewVisibility != View.VISIBLE
&& !isVisibleRequested()) {
- // Don't wait for GONE windows. However, we don't alter the state in case the window
- // becomes un-gone while the syncset is still active.
+ // Don't wait for invisible windows. However, we don't alter the state in case the
+ // window becomes visible while the sync group is still active.
return true;
}
return super.isSyncFinished();
diff --git a/services/core/xsd/display-device-config/autobrightness.xsd b/services/core/xsd/display-device-config/autobrightness.xsd
deleted file mode 100644
index 477625a..0000000
--- a/services/core/xsd/display-device-config/autobrightness.xsd
+++ /dev/null
@@ -1,33 +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.
--->
-<xs:schema version="2.0"
- elementFormDefault="qualified"
- xmlns:xs="http://www.w3.org/2001/XMLSchema">
- <xs:complexType name="autoBrightness">
- <xs:sequence>
- <!-- Sets the debounce for autoBrightness brightening in millis-->
- <xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
- minOccurs="0" maxOccurs="1">
- <xs:annotation name="final"/>
- </xs:element>
- <!-- Sets the debounce for autoBrightness darkening in millis-->
- <xs:element name="darkeningLightDebounceMillis" type="xs:nonNegativeInteger"
- minOccurs="0" maxOccurs="1">
- <xs:annotation name="final"/>
- </xs:element>
- </xs:sequence>
- </xs:complexType>
-</xs:schema>
\ No newline at end of file
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 bea5e2c..6b05d8f 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -23,7 +23,6 @@
<xs:schema version="2.0"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
- <xs:include schemaLocation="autobrightness.xsd" />
<xs:element name="displayConfiguration">
<xs:complexType>
<xs:sequence>
@@ -343,4 +342,74 @@
<xs:annotation name="final"/>
</xs:element>
</xs:complexType>
+
+ <xs:complexType name="autoBrightness">
+ <xs:sequence>
+ <!-- Sets the debounce for autoBrightness brightening in millis-->
+ <xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Sets the debounce for autoBrightness darkening in millis-->
+ <xs:element name="darkeningLightDebounceMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Sets the brightness mapping of the desired screen brightness in nits to the
+ corresponding lux for the current display -->
+ <xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- Represents the brightness mapping of the desired screen brightness in nits to the
+ corresponding lux for the current display -->
+ <xs:complexType name="displayBrightnessMapping">
+ <xs:sequence>
+ <!-- Sets the list of display brightness points, each representing the desired screen
+ brightness in nits to the corresponding lux for the current display
+
+ The N entries of this array define N + 1 control points as follows:
+ (1-based arrays)
+
+ Point 1: (0, nits[1]): currentLux <= 0
+ Point 2: (lux[1], nits[2]): 0 < currentLux <= lux[1]
+ Point 3: (lux[2], nits[3]): lux[2] < currentLux <= lux[3]
+ ...
+ Point N+1: (lux[N], nits[N+1]): lux[N] < currentLux
+
+ The control points must be strictly increasing. Each control point
+ corresponds to an entry in the brightness backlight values arrays.
+ For example, if currentLux == lux[1] (first element of the levels array)
+ then the brightness will be determined by nits[2] (second element
+ of the brightness values array).
+ -->
+ <xs:element name="displayBrightnessPoint" type="displayBrightnessPoint"
+ minOccurs="1" maxOccurs="unbounded">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- Represents a point in the display brightness mapping, representing the lux level from the
+ light sensor to the desired screen brightness in nits at this level -->
+ <xs:complexType name="displayBrightnessPoint">
+ <xs:sequence>
+ <!-- The lux level from the light sensor. This must be a non-negative integer -->
+ <xs:element name="lux" type="xs:nonNegativeInteger"
+ minOccurs="1" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+
+ <!-- Desired screen brightness in nits corresponding to the suggested lux values.
+ The display brightness is defined as the measured brightness of an all-white image.
+ This must be a non-negative integer -->
+ <xs:element name="nits" type="nonNegativeDecimal"
+ minOccurs="1" maxOccurs="1">
+ <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 e9a9269..fb7a920 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -5,8 +5,10 @@
ctor public AutoBrightness();
method public final java.math.BigInteger getBrighteningLightDebounceMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
+ method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
+ method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
}
public class BrightnessThresholds {
@@ -43,6 +45,19 @@
method public java.util.List<com.android.server.display.config.Density> getDensity();
}
+ public class DisplayBrightnessMapping {
+ ctor public DisplayBrightnessMapping();
+ method public final java.util.List<com.android.server.display.config.DisplayBrightnessPoint> getDisplayBrightnessPoint();
+ }
+
+ public class DisplayBrightnessPoint {
+ ctor public DisplayBrightnessPoint();
+ method public final java.math.BigInteger getLux();
+ method public final java.math.BigDecimal getNits();
+ method public final void setLux(java.math.BigInteger);
+ method public final void setNits(java.math.BigDecimal);
+ }
+
public class DisplayConfiguration {
ctor public DisplayConfiguration();
method @NonNull public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholds();
diff --git a/services/credentials/Android.bp b/services/credentials/Android.bp
new file mode 100644
index 0000000..5fa4442
--- /dev/null
+++ b/services/credentials/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.credentials-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.credentials",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.credentials-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
new file mode 100644
index 0000000..76ed2b3
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -0,0 +1,77 @@
+/*
+ * 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.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.credentials.ICredentialManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.SecureSettingsServiceNameResolver;
+
+/**
+ * Entry point service for credential management.
+ *
+ * <p>This service provides the {@link ICredentialManager} implementation and keeps a list of
+ * {@link CredentialManagerServiceImpl} per user; the real work is done by
+ * {@link CredentialManagerServiceImpl} itself.
+ */
+public final class CredentialManagerService extends
+ AbstractMasterSystemService<CredentialManagerService, CredentialManagerServiceImpl> {
+
+ private static final String TAG = "CredManSysService";
+
+ public CredentialManagerService(@NonNull Context context) {
+ super(context,
+ new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
+ null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
+ }
+
+ @Override
+ protected String getServiceSettingsProperty() {
+ return Settings.Secure.AUTOFILL_SERVICE;
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected CredentialManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
+ boolean disabled) {
+ return new CredentialManagerServiceImpl(this, mLock, resolvedUserId);
+ }
+
+ @Override
+ public void onStart() {
+ Log.i(TAG, "onStart");
+ }
+
+ final class CredentialManagerServiceStub extends ICredentialManager.Stub {
+ @Override
+ public void getCredential() {
+ final int userId = UserHandle.getCallingUserId();
+ synchronized (mLock) {
+ final CredentialManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ Log.i(TAG, "Got service for : " + userId);
+ service.getCredential();
+ }
+ }
+ }
+ }
+}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
new file mode 100644
index 0000000..f45f626
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.credentials;
+
+import android.annotation.NonNull;
+import android.util.Log;
+
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/**
+ * Per-user implementation of {@link CredentialManagerService}
+ */
+public class CredentialManagerServiceImpl extends
+ AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> {
+ private static final String TAG = "CredManSysServiceImpl";
+
+ protected CredentialManagerServiceImpl(
+ @NonNull CredentialManagerService master,
+ @NonNull Object lock, int userId) {
+ super(master, lock, userId);
+ }
+
+ /**
+ * Unimplemented getCredentials
+ */
+ public void getCredential() {
+ Log.i(TAG, "getCredential not implemented");
+ // TODO : Implement logic
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7b941d1..360653c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -671,6 +671,15 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
private static final long PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT = 165573442L;
+ /**
+ * For Admin Apps targeting U+
+ * If {@link android.security.IKeyChainService#setGrant} is called with an alias with no
+ * existing key, throw IllegalArgumentException.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private static final long THROW_EXCEPTION_WHEN_KEY_MISSING = 175101461L;
+
private static final String CREDENTIAL_MANAGEMENT_APP_INVALID_ALIAS_MSG =
"The alias provided must be contained in the aliases specified in the credential "
+ "management app's authentication policy";
@@ -5654,8 +5663,16 @@
final CallerIdentity caller = getCallerIdentity(callerPackage);
Preconditions.checkCallAuthorization(canChooseCertificates(caller));
-
- return setKeyChainGrantInternal(alias, hasGrant, Process.WIFI_UID, caller.getUserHandle());
+ try {
+ return setKeyChainGrantInternal(
+ alias, hasGrant, Process.WIFI_UID, caller.getUserHandle());
+ } catch (IllegalArgumentException e) {
+ if (mInjector.isChangeEnabled(THROW_EXCEPTION_WHEN_KEY_MISSING, caller.getPackageName(),
+ caller.getUserId())) {
+ throw e;
+ }
+ return false;
+ }
}
@Override
@@ -5705,8 +5722,15 @@
} catch (RemoteException e) {
throw new IllegalStateException("Failure getting grantee uid", e);
}
-
- return setKeyChainGrantInternal(alias, hasGrant, granteeUid, caller.getUserHandle());
+ try {
+ return setKeyChainGrantInternal(alias, hasGrant, granteeUid, caller.getUserHandle());
+ } catch (IllegalArgumentException e) {
+ if (mInjector.isChangeEnabled(THROW_EXCEPTION_WHEN_KEY_MISSING, packageName,
+ caller.getUserId())) {
+ throw e;
+ }
+ return false;
+ }
}
private boolean setKeyChainGrantInternal(String alias, boolean hasGrant, int granteeUid,
@@ -8312,9 +8336,8 @@
+ PackageManager.FEATURE_DEVICE_ADMIN + " feature.");
}
- // TODO(b/240562946): Remove owner name from API parameters.
@Override
- public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId,
+ public boolean setDeviceOwner(ComponentName admin, int userId,
boolean setProfileOwnerOnCurrentUserIfNecessary) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(admin)
@@ -14681,12 +14704,6 @@
if (parentUser == null) {
throw new IllegalStateException(String.format("User %d is not a profile", userId));
}
- if (!parentUser.isSystem()) {
- throw new IllegalStateException(
- String.format("Only the profile owner of a managed profile on the"
- + " primary user can be granted access to device identifiers, not"
- + " on user %d", parentUser.getIdentifier()));
- }
mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
isProfileOwnerOnOrganizationOwnedDevice,
@@ -18030,11 +18047,9 @@
}
- if (!setActiveAdminAndDeviceOwner(
- deviceOwnerUserId, deviceAdmin, provisioningParams.getOwnerName())) {
+ if (!setActiveAdminAndDeviceOwner(deviceOwnerUserId, deviceAdmin)) {
throw new ServiceSpecificException(
- ERROR_SET_DEVICE_OWNER_FAILED,
- "Failed to set device owner.");
+ ERROR_SET_DEVICE_OWNER_FAILED, "Failed to set device owner.");
}
disallowAddUser();
@@ -18161,12 +18176,12 @@
}
private boolean setActiveAdminAndDeviceOwner(
- @UserIdInt int userId, ComponentName adminComponent, String name) {
+ @UserIdInt int userId, ComponentName adminComponent) {
enableAndSetActiveAdmin(userId, userId, adminComponent);
// TODO(b/178187130): Directly set DO and remove the check once silent provisioning is no
// longer used.
if (getDeviceOwnerComponent(/* callingUserOnly= */ true) == null) {
- return setDeviceOwner(adminComponent, name, userId,
+ return setDeviceOwner(adminComponent, userId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ true);
}
return true;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 1fa2f53..f19dcde 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -256,7 +256,7 @@
mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
try {
- if (!mService.setDeviceOwner(mComponent, mName, mUserId,
+ if (!mService.setDeviceOwner(mComponent, mUserId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ !mSetDoOnly)) {
throw new RuntimeException(
"Can't set package " + mComponent + " as device owner.");
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 2f031bf..45ca5cd 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -216,7 +216,10 @@
if (!content) {
return {};
}
- return {content->data(), (int)content->size()};
+ // TODO(b/175635923): Replace with {content->data(), content->size()} after libc++ is upgraded.
+ // The type of the second std::span ctor param changed from ptrdiff_t to size_t between the old
+ // libc++ and the finalized C++20.
+ return std::span<const uint8_t>(content->data(), content->size());
}
binder::Status BinderIncrementalService::makeFile(
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index a49577b..6196c49 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1166,11 +1166,11 @@
if (!ifs) {
return -EINVAL;
}
- if (data.size() > params.size) {
+ if ((IncFsSize)data.size() > params.size) {
LOG(ERROR) << "Bad data size - bigger than file size";
return -EINVAL;
}
- if (!data.empty() && data.size() != params.size) {
+ if (!data.empty() && (IncFsSize)data.size() != params.size) {
// Writing a page is an irreversible operation, and it can't be updated with additional
// data later. Check that the last written page is complete, or we may break the file.
if (!isPageAligned(data.size())) {
@@ -3188,7 +3188,7 @@
}
FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
- return IncFs_FileIdFromMetadata({(const char*)metadata.data(), metadata.size()});
+ return IncFs_FileIdFromMetadata({(const char*)metadata.data(), (IncFsSize)metadata.size()});
}
} // namespace android::incremental
diff --git a/services/proguard.flags b/services/proguard.flags
index 606f360..27fe505 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -127,3 +127,7 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.usage.StorageStatsManagerLocal { *; }
-keep,allowoptimization,allowaccessmodification class com.android.internal.util.** { *; }
-keep,allowoptimization,allowaccessmodification class android.os.** { *; }
+
+# CoverageService guards optional jacoco class references with a runtime guard, so we can safely
+# suppress build-time warnings.
+-dontwarn org.jacoco.agent.rt.*
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
index 4ef6875..54c3861 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -628,7 +628,7 @@
CodePath.SAME, CodePath.DIFFERENT ->
throw AssertionError("secondDataPath cannot be a data path")
CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
- else -> {}
+ null -> {}
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java b/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java
new file mode 100644
index 0000000..9aa28ce
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java
@@ -0,0 +1,81 @@
+/*
+ * 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;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import android.util.Log;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Base class to make it easier to write tests that uses {@code ExtendedMockito}.
+ *
+ */
+public abstract class ExtendedMockitoTestCase {
+
+ private static final String TAG = ExtendedMockitoTestCase.class.getSimpleName();
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private MockitoSession mSession;
+
+ @Before
+ public void startSession() {
+ if (DEBUG) {
+ Log.d(TAG, "startSession()");
+ }
+ StaticMockitoSessionBuilder builder = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT);
+ initializeSession(builder);
+ mSession = builder.startMocking();
+ }
+
+ /**
+ * Initializes the mockito session.
+ *
+ * <p>Typically used to define which classes should have static methods mocked or spied.
+ */
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ if (DEBUG) {
+ Log.d(TAG, "initializeSession()");
+ }
+ }
+
+ @After
+ public final void finishSession() {
+ if (mSession == null) {
+ Log.w(TAG, "finishSession(): no session");
+ return;
+ }
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "finishSession()");
+ }
+ } finally {
+ // mSession.finishMocking() must ALWAYS be called (hence the over-protective try/finally
+ // statements), otherwise it would cause failures on future tests as mockito
+ // cannot start a session when a previous one is not finished
+ mSession.finishMocking();
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 130b02d..9bdc93e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -35,7 +35,9 @@
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
+import android.content.IIntentReceiver;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
@@ -48,6 +50,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
+import android.util.SparseArray;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -71,12 +74,14 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Common tests for {@link BroadcastQueue} implementations.
*/
@MediumTest
@RunWith(Parameterized.class)
+@SuppressWarnings("GuardedBy")
public class BroadcastQueueTest {
private static final String TAG = "BroadcastQueueTest";
@@ -92,15 +97,23 @@
private Context mContext;
private HandlerThread mHandlerThread;
+ private AtomicInteger mNextPid;
@Mock
private AppOpsService mAppOpsService;
@Mock
+ private ProcessList mProcessList;
+ @Mock
private PackageManagerInternal mPackageManagerInt;
private ActivityManagerService mAms;
private BroadcastQueue mQueue;
+ /**
+ * Map from PID to registered registered runtime receivers.
+ */
+ private SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
+
@Parameters(name = "impl={0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { {Impl.DEFAULT} });
@@ -118,6 +131,7 @@
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
+ mNextPid = new AtomicInteger(100);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
@@ -131,6 +145,18 @@
realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
realAms.mPackageManagerInt = mPackageManagerInt;
mAms = spy(realAms);
+ doAnswer((invocation) -> {
+ Log.v(TAG, "Intercepting startProcessLocked() for "
+ + Arrays.toString(invocation.getArguments()));
+ final String processName = invocation.getArgument(0);
+ final ApplicationInfo ai = invocation.getArgument(1);
+ final ProcessRecord res = makeActiveProcessRecord(ai, processName);
+ mHandlerThread.getThreadHandler().post(() -> {
+ mQueue.onApplicationAttachedLocked(res);
+ });
+ return res;
+ }).when(mAms).startProcessLocked(any(), any(), anyBoolean(), anyInt(),
+ any(), anyInt(), anyBoolean(), anyBoolean());
final BroadcastConstants constants = new BroadcastConstants(
Settings.Global.BROADCAST_FG_CONSTANTS);
@@ -142,10 +168,15 @@
return false;
}
};
+ final BroadcastHistory emptyHistory = new BroadcastHistory() {
+ public void addBroadcastToHistoryLocked(BroadcastRecord original) {
+ }
+ };
if (mImpl == Impl.DEFAULT) {
mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
- constants, emptySkipPolicy, false);
+ constants, emptySkipPolicy, emptyHistory, false,
+ ProcessList.SCHED_GROUP_DEFAULT);
} else {
throw new UnsupportedOperationException();
}
@@ -165,33 +196,65 @@
public Handler getUiHandler(ActivityManagerService service) {
return mHandlerThread.getThreadHandler();
}
+
+ @Override
+ public ProcessList getProcessList(ActivityManagerService service) {
+ return mProcessList;
+ }
}
private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception {
- final ProcessRecord r = new ProcessRecord(mAms, makeApplicationInfo(packageName), null,
- getUidForPackage(packageName));
+ final ApplicationInfo ai = makeApplicationInfo(packageName);
+ return makeActiveProcessRecord(ai, ai.processName);
+ }
+
+ private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, String processName)
+ throws Exception {
+ final ProcessRecord r = new ProcessRecord(mAms, ai, processName, ai.uid);
+ r.setPid(mNextPid.getAndIncrement());
+
final IApplicationThread thread = mock(IApplicationThread.class);
final IBinder threadBinder = new Binder();
doReturn(threadBinder).when(thread).asBinder();
r.makeActive(thread, mAms.mProcessStats);
-
doReturn(r).when(mAms).getProcessRecordLocked(eq(r.info.processName), eq(r.info.uid));
+ final IIntentReceiver receiver = mock(IIntentReceiver.class);
+ final IBinder receiverBinder = new Binder();
+ doReturn(receiverBinder).when(receiver).asBinder();
+ final ReceiverList receiverList = new ReceiverList(mAms, r, r.getPid(), r.info.uid,
+ UserHandle.getUserId(r.info.uid), receiver);
+ mRegisteredReceivers.put(r.getPid(), receiverList);
+
doAnswer((invocation) -> {
- Log.v(TAG, "Delivering finishReceiverLocked() for "
+ Log.v(TAG, "Intercepting scheduleReceiver() for "
+ Arrays.toString(invocation.getArguments()));
- mQueue.finishReceiverLocked(threadBinder, Activity.RESULT_OK,
- null, null, false, false);
+ mHandlerThread.getThreadHandler().post(() -> {
+ mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
+ null, null, false, false);
+ });
return null;
}).when(thread).scheduleReceiver(any(), any(), any(), anyInt(), any(), any(), anyBoolean(),
anyInt(), anyInt());
+ doAnswer((invocation) -> {
+ Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for "
+ + Arrays.toString(invocation.getArguments()));
+ mHandlerThread.getThreadHandler().post(() -> {
+ mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null, null,
+ false, false);
+ });
+ return null;
+ }).when(thread).scheduleRegisteredReceiver(any(), any(), anyInt(), any(), any(),
+ anyBoolean(), anyBoolean(), anyInt(), anyInt());
+
return r;
}
private ApplicationInfo makeApplicationInfo(String packageName) {
final ApplicationInfo ai = new ApplicationInfo();
ai.packageName = packageName;
+ ai.processName = packageName;
ai.uid = getUidForPackage(packageName);
return ai;
}
@@ -200,11 +263,22 @@
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = new ActivityInfo();
ri.activityInfo.packageName = packageName;
+ ri.activityInfo.processName = packageName;
ri.activityInfo.name = name;
ri.activityInfo.applicationInfo = makeApplicationInfo(packageName);
return ri;
}
+ private BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
+ final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
+ final IntentFilter filter = new IntentFilter();
+ final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
+ receiverList.app.info.packageName, null, null, null, receiverList.uid,
+ receiverList.userId, false, false, true);
+ receiverList.add(res);
+ return res;
+ }
+
private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
List receivers) {
return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(), receivers);
@@ -229,30 +303,46 @@
}
private void waitForIdle() throws Exception {
- for (int i = 0; i < 100; i++) {
- if (mQueue.isIdle()) break;
- SystemClock.sleep(100);
- }
- assertTrue(mQueue.isIdle());
+ mQueue.waitForIdle(null);
+ }
+
+ private void verifyScheduleReceiver(ProcessRecord app, Intent intent) throws Exception {
+ verify(app.getThread()).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(),
+ any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ }
+
+ private void verifyScheduleRegisteredReceiver(ProcessRecord app, Intent intent)
+ throws Exception {
+ verify(app.getThread()).scheduleRegisteredReceiver(any(),
+ argThat(filterEqualsIgnoringComponent(intent)), anyInt(), any(), any(),
+ anyBoolean(), anyBoolean(), eq(UserHandle.USER_SYSTEM), anyInt());
}
private static final String PACKAGE_RED = "com.example.red";
private static final String PACKAGE_GREEN = "com.example.green";
private static final String PACKAGE_BLUE = "com.example.blue";
+ private static final String PACKAGE_YELLOW = "com.example.yellow";
private static final String CLASS_RED = "com.example.red.Red";
private static final String CLASS_GREEN = "com.example.green.Green";
private static final String CLASS_BLUE = "com.example.blue.Blue";
+ private static final String CLASS_YELLOW = "com.example.yellow.Yellow";
private static int getUidForPackage(String packageName) {
switch (packageName) {
case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
+ case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
default: throw new IllegalArgumentException();
}
}
+ /**
+ * Verify dispatch of simple broadcast to single manifest receiver in
+ * already-running warm app.
+ */
@Test
public void testSimple_Manifest_Warm() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
@@ -263,11 +353,13 @@
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
waitForIdle();
- verify(receiverApp.getThread()).scheduleReceiver(
- argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(),
- any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ verifyScheduleReceiver(receiverApp, intent);
}
+ /**
+ * Verify dispatch of multiple broadcasts to multiple manifest receivers in
+ * already-running warm apps.
+ */
@Test
public void testSimple_Manifest_Warm_Multiple() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
@@ -285,20 +377,114 @@
List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
waitForIdle();
- verify(receiverGreenApp.getThread()).scheduleReceiver(
- argThat(filterEqualsIgnoringComponent(timezone)), any(), any(), anyInt(), any(),
- any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
- verify(receiverBlueApp.getThread()).scheduleReceiver(
- argThat(filterEqualsIgnoringComponent(timezone)), any(), any(), anyInt(), any(),
- any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
- verify(receiverBlueApp.getThread()).scheduleReceiver(
- argThat(filterEqualsIgnoringComponent(airplane)), any(), any(), anyInt(), any(),
- any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ verifyScheduleReceiver(receiverGreenApp, timezone);
+ verifyScheduleReceiver(receiverBlueApp, timezone);
+ verifyScheduleReceiver(receiverBlueApp, airplane);
}
- // TODO: verify registered receiver in warm app
- // TODO: verify manifest receiver in cold app
+ /**
+ * Verify dispatch of multiple broadcast to multiple manifest receivers in
+ * apps that require cold starts.
+ */
+ @Test
+ public void testSimple_Manifest_ColdThenWarm() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
- // TODO: verify mixing multiple manifest and registered receivers of same broadcast
- // TODO: verify delivery of 3 distinct broadcasts
+ // We purposefully dispatch into green twice; the first time cold and
+ // the second time it should already be running
+
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+
+ waitForIdle();
+ final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final ProcessRecord receiverBlueApp = mAms.getProcessRecordLocked(PACKAGE_BLUE,
+ getUidForPackage(PACKAGE_BLUE));
+ assertTrue(receiverBlueApp.getPid() > receiverGreenApp.getPid());
+ verifyScheduleReceiver(receiverGreenApp, timezone);
+ verifyScheduleReceiver(receiverGreenApp, airplane);
+ verifyScheduleReceiver(receiverBlueApp, timezone);
+ }
+
+ /**
+ * Verify dispatch of simple broadcast to single registered receiver in
+ * already-running warm app.
+ */
+ @Test
+ public void testSimple_Registered() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ final Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(intent, callerApp,
+ List.of(makeRegisteredReceiver(receiverApp))));
+
+ waitForIdle();
+ verifyScheduleRegisteredReceiver(receiverApp, intent);
+ }
+
+ /**
+ * Verify dispatch of multiple broadcasts to multiple registered receivers
+ * in already-running warm apps.
+ */
+ @Test
+ public void testSimple_Registered_Multiple() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeRegisteredReceiver(receiverGreenApp),
+ makeRegisteredReceiver(receiverBlueApp))));
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeRegisteredReceiver(receiverBlueApp))));
+
+ waitForIdle();
+ verifyScheduleRegisteredReceiver(receiverGreenApp, timezone);
+ verifyScheduleRegisteredReceiver(receiverBlueApp, timezone);
+ verifyScheduleRegisteredReceiver(receiverBlueApp, airplane);
+ }
+
+ /**
+ * Verify dispatch of multiple broadcasts mixed to both manifest and
+ * registered receivers, to both warm and cold apps.
+ */
+ @Test
+ public void testComplex() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+ final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
+
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeRegisteredReceiver(receiverGreenApp),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+ makeRegisteredReceiver(receiverYellowApp))));
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+
+ waitForIdle();
+ final ProcessRecord receiverBlueApp = mAms.getProcessRecordLocked(PACKAGE_BLUE,
+ getUidForPackage(PACKAGE_BLUE));
+ verifyScheduleReceiver(receiverGreenApp, timezone);
+ verifyScheduleRegisteredReceiver(receiverGreenApp, timezone);
+ verifyScheduleReceiver(receiverBlueApp, timezone);
+ verifyScheduleRegisteredReceiver(receiverYellowApp, timezone);
+ verifyScheduleReceiver(receiverYellowApp, airplane);
+ }
}
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 598c6b0..1c34548 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -317,11 +317,11 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class),
- any(ArraySet.class));
+ any(int[].class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class),
- any(ArraySet.class));
+ any(int[].class));
assertProcStates(app, PROCESS_STATE_RECEIVER, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 7755552..3866da3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -149,6 +149,12 @@
.thenReturn(mockArray);
when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerBottomRadiusArray))
.thenReturn(mockArray);
+ when(mMockedResources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
+ .thenReturn(mockArray);
+ when(mMockedResources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevels))
+ .thenReturn(new int[]{});
}
@After
@@ -792,11 +798,13 @@
@Test
public void testGetSystemPreferredDisplayMode() throws Exception {
SurfaceControl.DisplayMode displayMode1 = createFakeDisplayMode(0, 1920, 1080, 60f);
- // preferred mode
+ // system preferred mode
SurfaceControl.DisplayMode displayMode2 = createFakeDisplayMode(1, 3840, 2160, 60f);
+ // user preferred mode
+ SurfaceControl.DisplayMode displayMode3 = createFakeDisplayMode(2, 1920, 1080, 30f);
SurfaceControl.DisplayMode[] modes =
- new SurfaceControl.DisplayMode[]{displayMode1, displayMode2};
+ new SurfaceControl.DisplayMode[]{displayMode1, displayMode2, displayMode3};
FakeDisplay display = new FakeDisplay(PORT_A, modes, 0, 1);
setUpDisplay(display);
updateAvailableDisplays();
@@ -808,24 +816,43 @@
DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
0).getDisplayDeviceInfoLocked();
-
assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
-
Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(matches(defaultMode, displayMode1)).isTrue();
+
+ // Set the user preferred display mode
+ mListener.addedDisplays.get(0).setUserPreferredDisplayModeLocked(
+ new Display.Mode(
+ displayMode3.width, displayMode3.height, displayMode3.refreshRate));
+ updateAvailableDisplays();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ displayDeviceInfo = mListener.addedDisplays.get(
+ 0).getDisplayDeviceInfoLocked();
+ defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(matches(defaultMode, displayMode3)).isTrue();
+
+ // clear the user preferred mode
+ mListener.addedDisplays.get(0).setUserPreferredDisplayModeLocked(null);
+ updateAvailableDisplays();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ displayDeviceInfo = mListener.addedDisplays.get(
+ 0).getDisplayDeviceInfoLocked();
+ defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
assertThat(matches(defaultMode, displayMode2)).isTrue();
- // Change the display and add new preferred mode
- SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(2, 2340, 1080, 60f);
- modes = new SurfaceControl.DisplayMode[]{displayMode1, displayMode2, addedDisplayInfo};
+ // Change the display and add new system preferred mode
+ SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3, 2340, 1080, 20f);
+ modes = new SurfaceControl.DisplayMode[]{
+ displayMode1, displayMode2, displayMode3, addedDisplayInfo};
display.dynamicInfo.supportedDisplayModes = modes;
- display.dynamicInfo.preferredBootDisplayMode = 2;
+ display.dynamicInfo.preferredBootDisplayMode = 3;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
assertTrue(mListener.traversalRequested);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
- assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(3);
DisplayDevice displayDevice = mListener.changedDisplays.get(0);
displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
new file mode 100644
index 0000000..6493111
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.pm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.test.annotation.UiThreadTest;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.LocalServices;
+import com.android.server.am.UserState;
+import com.android.server.pm.UserManagerService.UserData;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+/**
+ * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest}
+ */
+public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
+
+ private static final String TAG = UserManagerServiceTest.class.getSimpleName();
+
+ private final Object mPackagesLock = new Object();
+ private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
+ .getTargetContext();
+ private Context mSpiedContext;
+
+ private @Mock PackageManagerService mMockPms;
+ private @Mock UserDataPreparer mMockUserDataPreparer;
+ private @Mock ActivityManagerInternal mActivityManagerInternal;
+
+ private final SparseArray<UserData> mUsers = new SparseArray<>();
+ private UserManagerService mUms;
+ private UserManagerInternal mUmi;
+
+ @Override
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ builder
+ .spyStatic(UserManager.class)
+ .spyStatic(LocalServices.class);
+ }
+
+ @Before
+ @UiThreadTest // Needed to initialize main handler
+ public void setFixtures() {
+ mSpiedContext = spy(mRealContext);
+
+ // Called when WatchedUserStates is constructed
+ doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache());
+
+ mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
+ mPackagesLock, mRealContext.getDataDir(), mUsers);
+ mUmi = LocalServices.getService(UserManagerInternal.class);
+
+ assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mUmi)
+ .isNotNull();
+ }
+
+ @After
+ public void resetLocalService() {
+ // LocalServices follows the "Highlander rule" - There can be only one!
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ }
+
+ @Test
+ public void testgetCurrentUserId_amInternalNotReady() {
+ mockGetLocalService(ActivityManagerInternal.class, null);
+
+ assertWithMessage("getCurrentUserId()").that(mUms.getCurrentUserId())
+ .isEqualTo(UserHandle.USER_NULL);
+ }
+
+ @Test
+ public void testgetCurrentUserId() {
+ mockCurrentUser(42);
+
+ assertWithMessage("getCurrentUserId()").that(mUms.getCurrentUserId())
+ .isEqualTo(42);
+ }
+
+ @Test
+ public void testIsCurrentUserOrRunningProfileOfCurrentUser_currentUser() {
+ int userId = 42;
+ mockCurrentUser(userId);
+
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(userId)).isTrue();
+ }
+
+ @Test
+ public void testIsCurrentUserOrRunningProfileOfCurrentUser_notCurrentUser() {
+ int userId = 42;
+ mockCurrentUser(108);
+
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(userId)).isFalse();
+ }
+
+ @Test
+ public void testIsCurrentUserOrRunningProfileOfCurrentUser_startedProfileOfCurrentUser() {
+ int parentId = 108;
+ int profileId = 42;
+ addUser(parentId);
+ addProfile(profileId, parentId);
+ mockCurrentUser(parentId);
+ setUserState(profileId, UserState.STATE_RUNNING_UNLOCKED);
+
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isTrue();
+ }
+
+ @Test
+ public void testIsCurrentUserOrRunningProfileOfCurrentUser_stoppedProfileOfCurrentUser() {
+ int parentId = 108;
+ int profileId = 42;
+ addUser(parentId);
+ addProfile(profileId, parentId);
+ mockCurrentUser(parentId);
+ // TODO(b/244798930): should set it to STATE_STOPPING or STATE_SHUTDOWN instead
+ removeUserState(profileId);
+
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isFalse();
+ }
+
+ @Test
+ public void testIsCurrentUserOrRunningProfileOfCurrentUser_profileOfNonCurrentUSer() {
+ int parentId = 108;
+ int profileId = 42;
+ int currentUserId = 666;
+ addUser(parentId);
+ addProfile(profileId, parentId);
+ mockCurrentUser(currentUserId);
+
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isFalse();
+ }
+
+ private void mockCurrentUser(@UserIdInt int userId) {
+ mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
+
+ when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId);
+ }
+
+ private <T> void mockGetLocalService(Class<T> serviceClass, T service) {
+ doReturn(service).when(() -> LocalServices.getService(serviceClass));
+ }
+
+ private void addProfile(@UserIdInt int profileId, @UserIdInt int parentId) {
+ TestUserData profileData = new TestUserData(profileId);
+ profileData.info.flags = UserInfo.FLAG_PROFILE;
+ profileData.info.profileGroupId = parentId;
+
+ addUserData(profileData);
+ }
+
+ private void addUser(@UserIdInt int userId) {
+ TestUserData userData = new TestUserData(userId);
+
+ addUserData(userData);
+ }
+
+ private void addUserData(TestUserData userData) {
+ Log.d(TAG, "Adding " + userData);
+ mUsers.put(userData.info.id, userData);
+ }
+
+ private void setUserState(@UserIdInt int userId, int userState) {
+ mUmi.setUserState(userId, userState);
+ }
+
+ private void removeUserState(@UserIdInt int userId) {
+ mUmi.removeUserState(userId);
+ }
+
+ private static final class TestUserData extends UserData {
+
+ @SuppressWarnings("unused")
+ TestUserData(@UserIdInt int userId) {
+ info = new UserInfo();
+ info.id = userId;
+ }
+
+ @Override
+ public String toString() {
+ return "TestUserData[" + info.toFullString() + "]";
+ }
+ }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 0483a60..7ae70eb 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -147,6 +147,19 @@
android:resource="@xml/test_dream_metadata" />
</service>
+ <service
+ android:name="com.android.server.dreams.TestDreamServiceWithInvalidSettings"
+ android:exported="false"
+ android:label="Test Dream" >
+ <intent-filter>
+ <action android:name="android.service.dreams.DreamService" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data
+ android:name="android.service.dream"
+ android:resource="@xml/test_dream_metadata_invalid" />
+ </service>
+
<receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN"
android:exported="true">
diff --git a/services/tests/servicestests/res/xml/test_dream_metadata.xml b/services/tests/servicestests/res/xml/test_dream_metadata.xml
index aa054f1..9905c69 100644
--- a/services/tests/servicestests/res/xml/test_dream_metadata.xml
+++ b/services/tests/servicestests/res/xml/test_dream_metadata.xml
@@ -15,5 +15,5 @@
-->
<dream xmlns:android="http://schemas.android.com/apk/res/android"
- android:settingsActivity="com.android.server.dreams/.TestDreamSettingsActivity"
+ android:settingsActivity="com.android.frameworks.servicestests/.TestDreamSettingsActivity"
android:showClockAndComplications="false" />
diff --git a/services/tests/servicestests/res/xml/test_dream_metadata_invalid.xml b/services/tests/servicestests/res/xml/test_dream_metadata_invalid.xml
new file mode 100644
index 0000000..47864d9
--- /dev/null
+++ b/services/tests/servicestests/res/xml/test_dream_metadata_invalid.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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.
+ -->
+
+<!-- The settings activity is in a different package, which is invalid -->
+<dream xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="com.android.server.dreams/.TestDreamSettingsActivity"
+ android:showClockAndComplications="false"/>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index c7757f7..acbcad5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId;
import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges;
import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId;
@@ -587,10 +588,12 @@
verify(mMockA11yEventSender, times(2))
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
- allOf(a11yWindowId(currentActiveWindowId),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(currentActiveWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
assertThat(captor.getAllValues().get(1),
- allOf(a11yWindowId(eventWindowId),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
}
@@ -600,7 +603,7 @@
DEFAULT_FOCUSED_INDEX);
final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId(
AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
- assertThat(currentA11yFocusedWindowId, is(not(eventWindowId)));
+ assertThat(currentA11yFocusedWindowId, is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID));
final int noUse = 0;
mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
@@ -612,14 +615,65 @@
AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
final ArgumentCaptor<AccessibilityEvent> captor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
+ a11yWindowChanges(
+ AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary()
+ throws RemoteException {
+ runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID);
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault()
+ throws RemoteException {
+ runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ SECONDARY_DISPLAY_ID, Display.DEFAULT_DISPLAY);
+ }
+
+ private void runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ int initialDisplayId, int eventDisplayId) throws RemoteException {
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+ final int initialWindowId = getWindowIdFromWindowInfosForDisplay(
+ initialDisplayId, DEFAULT_FOCUSED_INDEX);
+ final int noUse = 0;
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ initialWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(initialWindowId));
+ Mockito.reset(mMockA11yEventSender);
+
+ final int eventWindowId = getWindowIdFromWindowInfosForDisplay(
+ eventDisplayId, DEFAULT_FOCUSED_INDEX);
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mMockA11yEventSender, times(2))
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
- allOf(a11yWindowId(currentA11yFocusedWindowId),
+ allOf(displayId(initialDisplayId),
+ a11yWindowId(initialWindowId),
a11yWindowChanges(
AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
assertThat(captor.getAllValues().get(1),
- allOf(a11yWindowId(eventWindowId),
+ allOf(displayId(eventDisplayId),
+ a11yWindowId(eventWindowId),
a11yWindowChanges(
AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
}
@@ -674,10 +728,12 @@
verify(mMockA11yEventSender, times(2))
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
- allOf(a11yWindowId(eventWindowId),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
assertThat(captor.getAllValues().get(1),
- allOf(a11yWindowId(currentActiveWindowId),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(currentActiveWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
}
@@ -861,6 +917,77 @@
assertTrue(TextUtils.equals(layoutParams.accessibilityTitle, a11yWindow.getTitle()));
}
+ @Test
+ public void sendAccessibilityEventOnWindowRemoval() {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+
+ // Removing index 0 because it's not focused, and avoids unnecessary layer change.
+ final int windowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
+ infos.remove(0);
+ for (WindowInfo info : infos) {
+ // Adjust layer number because it should start from 0.
+ info.layer--;
+ }
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED)));
+ }
+
+ @Test
+ public void sendAccessibilityEventOnWindowAddition() throws RemoteException {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+
+ for (WindowInfo info : infos) {
+ // Adjust layer number because new window will have 0 so that layer number in
+ // A11yWindowInfo in window won't be changed.
+ info.layer++;
+ }
+
+ final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, USER_SYSTEM_ID);
+ addWindowInfo(infos, token, 0);
+ final int windowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED)));
+ }
+
+ @Test
+ public void sendAccessibilityEventOnWindowChange() {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+ infos.get(0).title = "new title";
+ final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE)));
+ }
+
private void registerLeashedTokenAndWindowId() {
mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
@@ -1018,6 +1145,29 @@
}
}
+ static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+ private final int mDisplayId;
+
+ DisplayIdMatcher(int displayId) {
+ super();
+ mDisplayId = displayId;
+ }
+
+ static DisplayIdMatcher displayId(int displayId) {
+ return new DisplayIdMatcher(displayId);
+ }
+
+ @Override
+ protected boolean matchesSafely(AccessibilityEvent event) {
+ return event.getDisplayId() == mDisplayId;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to displayId " + mDisplayId);
+ }
+ }
+
static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
private int mWindowId;
diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
index b17c3a1..428eaff 100644
--- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
@@ -55,14 +55,20 @@
mMockAudioService = mock(AudioService.class);
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
- mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem);
+ mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem,
+ false /*headTrackingEnabledByDefault*/);
}
+ /**
+ * Test that constructing an SADeviceState instance requires a non-null address for a
+ * wireless type, but can take null for a non-wireless type;
+ * @throws Exception
+ */
@Test
public void testSADeviceStateNullAddressCtor() throws Exception {
try {
- SADeviceState devState = new SADeviceState(
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null);
+ SADeviceState devState = new SADeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null);
+ devState = new SADeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, null);
Assert.fail();
} catch (NullPointerException e) { }
}
@@ -88,11 +94,12 @@
final AudioDeviceAttributes dev1 =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
final AudioDeviceAttributes dev2 =
- new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "C3:P0:beep");
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "C3:PO:beep");
final AudioDeviceAttributes dev3 =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "R2:D2:bloop");
doNothing().when(mMockAudioService).persistSpatialAudioDeviceSettings();
+ mSpatHelper.initForTest(true /*binaural*/, true /*transaural*/);
// test with single device
mSpatHelper.addCompatibleAudioDevice(dev1);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index eda9133..4a363c2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -966,7 +966,7 @@
// Set admin1 to active admin and device owner
dpm.setActiveAdmin(admin1, false);
- dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM);
+ dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM);
// Save password history length
dpm.setPasswordHistoryLength(admin1, passwordHistoryLength);
@@ -1124,7 +1124,7 @@
dpm.setActiveAdmin(admin1, /* replace =*/ false);
// Fire!
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
// getDeviceOwnerComponent should return the admin1 component.
assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1);
@@ -1181,7 +1181,7 @@
// DO needs to be a DA
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
// DO should be set on headless system user
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
// PO should be set on calling user.
assertThat(dpm.getProfileOwnerAsUser(CALLER_USER_HANDLE)).isEqualTo(admin1);
});
@@ -1353,7 +1353,7 @@
assertExpectException(IllegalArgumentException.class,
/* messageRegex= */ "Invalid component",
- () -> dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"), /* ownerName= */ null,
+ () -> dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"),
UserHandle.USER_SYSTEM));
}
@@ -1363,13 +1363,13 @@
// Package doesn't exist and caller is not system
assertExpectException(SecurityException.class,
/* messageRegex= */ "Calling identity is not authorized",
- () -> dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM));
+ () -> dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM));
// Package exists, but caller is not system
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
assertExpectException(SecurityException.class,
/* messageRegex= */ "Calling identity is not authorized",
- () -> dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM));
+ () -> dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM));
}
@Test
@@ -1389,7 +1389,7 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
@@ -1452,7 +1452,7 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
verify(getServices().ibackupManager, times(1)).setBackupServiceActive(
eq(UserHandle.USER_SYSTEM), eq(false));
@@ -1493,7 +1493,7 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
// Now call clear from the secondary user, which should throw.
@@ -1527,7 +1527,7 @@
// Set admin1 to active admin and device owner
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM);
+ dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM);
// Add reset password token
final long handle = 12000;
@@ -1566,8 +1566,7 @@
dpm.setActiveAdmin(admin2, /* refreshing= */ true, CALLER_USER_HANDLE);
assertExpectException(IllegalStateException.class,
/* messageRegex= */ "already has a profile owner",
- () -> dpm.setDeviceOwner(admin2, "owner-name",
- CALLER_USER_HANDLE));
+ () -> dpm.setDeviceOwner(admin2, CALLER_USER_HANDLE));
});
}
@@ -1656,7 +1655,7 @@
// Set DO on the system user which is only allowed during first boot.
setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
- assertThat(dpm.setDeviceOwner(admin3, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin3, UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false)).isEqualTo(admin3);
// Then check getDeviceOwnerAdminLocked().
@@ -1979,8 +1978,7 @@
// Call.
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name",
- UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertNoDeviceOwnerRestrictions();
reset(getServices().userManagerInternal);
@@ -2358,8 +2356,7 @@
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name",
- UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertNoDeviceOwnerRestrictions();
@@ -2695,7 +2692,7 @@
dpm.clearProfileOwner(admin1);
dpm.setActiveAdmin(admin1, false);
// Test 4, Caller is DO now.
- assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
// 4-1. But WifiManager is not ready.
assertThat(dpm.getWifiMacAddress(admin1)).isNull();
@@ -2745,7 +2742,7 @@
// Remove PO and add DO.
dpm.clearProfileOwner(admin1);
dpm.setActiveAdmin(admin1, false);
- assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
// admin1 is DO.
// Set current call state of device to ringing.
@@ -3030,7 +3027,7 @@
// Set a device owner on the system user. Check that the system user becomes affiliated.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpm.isAffiliatedUser()).isTrue();
assertThat(dpm.getAffiliationIds(admin1).isEmpty()).isTrue();
@@ -3254,7 +3251,7 @@
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, false);
- assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
}
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 03ea613..66420ad 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -19,16 +19,19 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,22 +55,16 @@
private Resources mResources;
@Before
- public void setUp() throws IOException {
+ public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
mockDeviceConfigs();
- try {
- Path tempFile = Files.createTempFile("display_config", ".tmp");
- Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8));
- mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
- mDisplayDeviceConfig.initFromFile(tempFile.toFile());
- } catch (IOException e) {
- throw new IOException("Failed to setup the display device config.", e);
- }
}
@Test
- public void testConfigValues() {
+ public void testConfigValuesFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
assertEquals(mDisplayDeviceConfig.getAmbientHorizonLong(), 5000);
assertEquals(mDisplayDeviceConfig.getAmbientHorizonShort(), 50);
assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000);
@@ -88,10 +85,23 @@
assertEquals(mDisplayDeviceConfig.getScreenDarkeningMinThreshold(), 0.002, 0.000001f);
assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
-
+ assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
+ float[]{0.0f, 50.0f, 80.0f}, 0.0f);
+ assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
+ float[]{45.32f, 75.43f}, 0.0f);
// Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
- // Also add test for the case where optional display configs are null
+ }
+
+ @Test
+ public void testConfigValuesFromConfigResource() {
+ setupDisplayDeviceConfigFromConfigResourceFile();
+ assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
+ float[]{2.0f, 200.0f, 600.0f}, 0.0f);
+ assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
+ float[]{0.0f, 0.0f, 110.0f, 500.0f}, 0.0f);
+ // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
+ // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
private String getContent() {
@@ -114,6 +124,16 @@
+ "<autoBrightness>\n"
+ "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
+ + "<displayBrightnessMapping>\n"
+ + "<displayBrightnessPoint>\n"
+ + "<lux>50</lux>\n"
+ + "<nits>45.32</nits>\n"
+ + "</displayBrightnessPoint>\n"
+ + "<displayBrightnessPoint>\n"
+ + "<lux>80</lux>\n"
+ + "<nits>75.43</nits>\n"
+ + "</displayBrightnessPoint>\n"
+ + "</displayBrightnessMapping>\n"
+ "</autoBrightness>\n"
+ "<highBrightnessMode enabled=\"true\">\n"
+ "<transitionPoint>0.62</transitionPoint>\n"
@@ -185,4 +205,63 @@
when(mResources.getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingMaximumFloat)).thenReturn(1.0f);
}
+
+ private void setupDisplayDeviceConfigFromDisplayConfigFile() throws IOException {
+ Path tempFile = Files.createTempFile("display_config", ".tmp");
+ Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8));
+ mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
+ mDisplayDeviceConfig.initFromFile(tempFile.toFile());
+ }
+
+ private void setupDisplayDeviceConfigFromConfigResourceFile() {
+ TypedArray screenBrightnessNits = createFloatTypedArray(new float[]{2.0f, 250.0f, 650.0f});
+ when(mResources.obtainTypedArray(
+ com.android.internal.R.array.config_screenBrightnessNits))
+ .thenReturn(screenBrightnessNits);
+ TypedArray screenBrightnessBacklight = createFloatTypedArray(new
+ float[]{0.0f, 120.0f, 255.0f});
+ when(mResources.obtainTypedArray(
+ com.android.internal.R.array.config_screenBrightnessBacklight))
+ .thenReturn(screenBrightnessBacklight);
+ when(mResources.getIntArray(com.android.internal.R.array
+ .config_screenBrightnessBacklight)).thenReturn(new int[]{0, 120, 255});
+
+ when(mResources.getIntArray(com.android.internal.R.array
+ .config_autoBrightnessLevels)).thenReturn(new int[]{30, 80});
+ when(mResources.getIntArray(com.android.internal.R.array
+ .config_autoBrightnessDisplayValuesNits)).thenReturn(new int[]{25, 55});
+
+ TypedArray screenBrightnessLevelNits = createFloatTypedArray(new
+ float[]{2.0f, 200.0f, 600.0f});
+ when(mResources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
+ .thenReturn(screenBrightnessLevelNits);
+ int[] screenBrightnessLevelLux = new int[]{0, 110, 500};
+ when(mResources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevels))
+ .thenReturn(screenBrightnessLevelLux);
+
+ mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
+
+ }
+
+ private TypedArray createFloatTypedArray(float[] vals) {
+ TypedArray mockArray = mock(TypedArray.class);
+ when(mockArray.length()).thenAnswer(invocation -> {
+ return vals.length;
+ });
+ when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> {
+ final float def = (float) invocation.getArguments()[1];
+ if (vals == null) {
+ return def;
+ }
+ int idx = (int) invocation.getArguments()[0];
+ if (idx >= 0 && idx < vals.length) {
+ return vals[idx];
+ } else {
+ return def;
+ }
+ });
+ return mockArray;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java
index 74d2e0f..0efd296 100644
--- a/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java
@@ -16,7 +16,8 @@
package com.android.server.dreams;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
import android.content.ComponentName;
@@ -35,21 +36,36 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamServiceTest {
+ private static final String TEST_PACKAGE_NAME = "com.android.frameworks.servicestests";
+
@Test
public void testMetadataParsing() throws PackageManager.NameNotFoundException {
- final String testPackageName = "com.android.frameworks.servicestests";
final String testDreamClassName = "com.android.server.dreams.TestDreamService";
- final String testSettingsActivity = "com.android.server.dreams/.TestDreamSettingsActivity";
+ final String testSettingsActivity =
+ "com.android.frameworks.servicestests/.TestDreamSettingsActivity";
+ final DreamService.DreamMetadata metadata = getDreamMetadata(testDreamClassName);
- final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- final ServiceInfo si = context.getPackageManager().getServiceInfo(
- new ComponentName(testPackageName, testDreamClassName),
- PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
- final DreamService.DreamMetadata metadata = DreamService.getDreamMetadata(context, si);
-
- assertEquals(0, metadata.settingsActivity.compareTo(
- ComponentName.unflattenFromString(testSettingsActivity)));
+ assertThat(metadata.settingsActivity).isEqualTo(
+ ComponentName.unflattenFromString(testSettingsActivity));
assertFalse(metadata.showComplications);
}
+
+ @Test
+ public void testMetadataParsing_invalidSettingsActivity()
+ throws PackageManager.NameNotFoundException {
+ final String testDreamClassName =
+ "com.android.server.dreams.TestDreamServiceWithInvalidSettings";
+ final DreamService.DreamMetadata metadata = getDreamMetadata(testDreamClassName);
+
+ assertThat(metadata.settingsActivity).isNull();
+ }
+
+ private DreamService.DreamMetadata getDreamMetadata(String dreamClassName)
+ throws PackageManager.NameNotFoundException {
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final ServiceInfo si = context.getPackageManager().getServiceInfo(
+ new ComponentName(TEST_PACKAGE_NAME, dreamClassName),
+ PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
+ return DreamService.getDreamMetadata(context, si);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/services/tests/servicestests/src/com/android/server/dreams/TestDreamServiceWithInvalidSettings.java
similarity index 62%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
rename to services/tests/servicestests/src/com/android/server/dreams/TestDreamServiceWithInvalidSettings.java
index e62a63a..5c7d02f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/services/tests/servicestests/src/com/android/server/dreams/TestDreamServiceWithInvalidSettings.java
@@ -14,18 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode;
+package com.android.server.dreams;
-import android.os.SystemProperties;
+import android.service.dreams.DreamService;
/**
- * Constants for desktop mode feature
+ * Dream service implementation for unit testing, where the settings activity is invalid.
*/
-public class DesktopModeConstants {
-
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+public class TestDreamServiceWithInvalidSettings extends DreamService {
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index b1ad8ec..e04edc6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.os.UserManager.DISALLOW_BLUETOOTH;
import static android.os.UserManager.DISALLOW_USER_SWITCH;
import static com.google.common.truth.Truth.assertThat;
@@ -40,6 +41,7 @@
import com.android.server.LocalServices;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -174,6 +176,21 @@
}
@Test
+ public void testSetUserRestrictionWithIncorrectID() throws Exception {
+ int incorrectId = 1;
+ while (mUserManagerService.userExists(incorrectId)) {
+ incorrectId++;
+ }
+ try {
+ mUserManagerService.setUserRestriction(DISALLOW_BLUETOOTH, true, incorrectId);
+ Assert.fail();
+ } catch (IllegalArgumentException e) {
+ //Exception is expected to be thrown if user ID does not exist.
+ // IllegalArgumentException thrown means this test is successful.
+ }
+ }
+
+ @Test
public void assertIsUserSwitcherEnabledOnMultiUserSettings() throws Exception {
int userId = ActivityManager.getCurrentUser();
resetUserSwitcherEnabled();
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 722b504..8d53b71 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -121,6 +121,10 @@
public class VibratorManagerServiceTest {
private static final int TEST_TIMEOUT_MILLIS = 1_000;
+ // Time to allow for a cancellation to complete (notably including system ramp down), but not so
+ // long that tests easily get really slow or flaky. If a vibration is close to this, it should
+ // be cancelled in the body of the individual test.
+ private static final int CLEANUP_TIMEOUT_MILLIS = 100;
private static final int UID = Process.ROOT_UID;
private static final int VIRTUAL_DISPLAY_ID = 1;
private static final String PACKAGE_NAME = "package";
@@ -166,6 +170,7 @@
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
+ private VibratorManagerService mService;
private Context mContextSpy;
private TestLooper mTestLooper;
private FakeVibrator mVibrator;
@@ -230,8 +235,27 @@
@After
public void tearDown() throws Exception {
+ if (mService != null) {
+ // Wait until all vibrators have stopped vibrating, with a bit of flexibility for tests
+ // that just do a click or have cancelled at the end (waiting for ramp-down).
+ //
+ // Note: if a test is flaky here, check whether a VibrationEffect duration is close to
+ // CLEANUP_TIMEOUT_MILLIS - in which case it's probably best to just cancel that effect
+ // explicitly at the end of the test case (rather than letting it run and race flakily).
+ assertTrue(waitUntil(s -> {
+ for (int vibratorId : mService.getVibratorIds()) {
+ if (s.isVibrating(vibratorId)) {
+ return false;
+ }
+ }
+ return true;
+ }, mService, CLEANUP_TIMEOUT_MILLIS));
+ }
+
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ // Ignore potential exceptions about the looper having never dispatched any messages.
+ mTestLooper.stopAutoDispatchAndIgnoreExceptions();
}
private VibratorManagerService createSystemReadyService() {
@@ -241,7 +265,7 @@
}
private VibratorManagerService createService() {
- return new VibratorManagerService(
+ mService = new VibratorManagerService(
mContextSpy,
new VibratorManagerService.Injector() {
@Override
@@ -278,6 +302,7 @@
(VibratorManagerService.ExternalVibratorService) serviceInstance;
}
});
+ return mService;
}
@Test
@@ -478,6 +503,7 @@
verify(listeners[0]).onVibrating(eq(true));
verify(listeners[1]).onVibrating(eq(true));
verify(listeners[2], never()).onVibrating(eq(true));
+ cancelVibrate(service);
}
@Test
@@ -804,6 +830,7 @@
assertFalse(
mVibratorProviders.get(1).getAllEffectSegments().stream()
.anyMatch(segment -> segment instanceof PrebakedSegment));
+ cancelVibrate(service); // Clean up repeating effect.
}
@Test
@@ -830,6 +857,8 @@
// The second vibration should have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
+
+ cancelVibrate(service); // Clean up repeating effect.
}
@Test
@@ -855,6 +884,8 @@
assertFalse(
mVibratorProviders.get(1).getAllEffectSegments().stream()
.anyMatch(segment -> segment instanceof PrebakedSegment));
+
+ cancelVibrate(service); // Clean up long effect.
}
@Test
@@ -1194,10 +1225,11 @@
// Vibration is not stopped nearly after updating service.
assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
+ cancelVibrate(service);
}
@Test
- public void vibrate_withVitualDisplayChange_ignoreVibrationFromVirtualDisplay()
+ public void vibrate_withVirtualDisplayChange_ignoreVibrationFromVirtualDisplay()
throws Exception {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
@@ -1223,10 +1255,11 @@
// Haptic feedback played normally when the virtual display is removed.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ cancelVibrate(service); // Clean up long-ish effect.
}
@Test
- public void vibrate_withAppsOnVitualDisplayChange_ignoreVibrationFromVirtualDisplay()
+ public void vibrate_withAppsOnVirtualDisplayChange_ignoreVibrationFromVirtualDisplay()
throws Exception {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
@@ -1251,7 +1284,7 @@
HAPTIC_FEEDBACK_ATTRS);
// Haptic feedback played normally when the same app no long runs on a virtual display.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
-
+ cancelVibrate(service); // Clean up long-ish effect.
}
@Test
@@ -1935,6 +1968,10 @@
when(mNativeWrapperMock.getVibratorIds()).thenReturn(vibratorIds);
}
+ private void cancelVibrate(VibratorManagerService service) {
+ service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
+ }
+
private IVibratorStateListener mockVibratorStateListener() {
IVibratorStateListener listenerMock = mock(IVibratorStateListener.class);
IBinder binderMock = mock(IBinder.class);
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 894a901..cae8fd9 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6199,6 +6199,62 @@
}
@Test
+ public void testCannotRemoveForegroundFlagWhenOverLimit_enqueued() {
+ for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
+ Notification n = new Notification.Builder(mContext, "").build();
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ mService.addEnqueuedNotification(r);
+ }
+ Notification n = new Notification.Builder(mContext, "").build();
+ n.flags |= FLAG_FOREGROUND_SERVICE;
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG,
+ NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addEnqueuedNotification(r);
+
+ mInternalService.removeForegroundServiceFlagFromNotification(
+ PKG, r.getSbn().getId(), r.getSbn().getUserId());
+
+ waitForIdle();
+
+ assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS,
+ mService.getNotificationRecordCount());
+ }
+
+ @Test
+ public void testCannotRemoveForegroundFlagWhenOverLimit_posted() {
+ for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
+ Notification n = new Notification.Builder(mContext, "").build();
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ mService.addNotification(r);
+ }
+ Notification n = new Notification.Builder(mContext, "").build();
+ n.flags |= FLAG_FOREGROUND_SERVICE;
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG,
+ NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addNotification(r);
+
+ mInternalService.removeForegroundServiceFlagFromNotification(
+ PKG, r.getSbn().getId(), r.getSbn().getUserId());
+
+ waitForIdle();
+
+ assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS,
+ mService.getNotificationRecordCount());
+ }
+
+ @Test
public void testAllowForegroundCustomToasts() throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
@@ -7548,6 +7604,43 @@
}
@Test
+ public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception {
+ mService.isSystemUid = true;
+ ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ mService.setZenHelper(mockZenModeHelper);
+ ComponentName owner = new ComponentName("android", "ProviderName");
+ ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
+ boolean isEnabled = true;
+ AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
+ zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
+ mBinderService.addAutomaticZenRule(rule, "com.android.settings");
+
+ // verify that zen mode helper gets passed in a package name of "android"
+ verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
+ }
+
+ @Test
+ public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception {
+ mService.isSystemUid = false;
+ ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ mService.setZenHelper(mockZenModeHelper);
+ ComponentName owner = new ComponentName("android", "ProviderName");
+ ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
+ boolean isEnabled = true;
+ AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
+ zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
+ mBinderService.addAutomaticZenRule(rule, "another.package");
+
+ // verify that zen mode helper gets passed in the package name from the arg, not the owner
+ verify(mockZenModeHelper).addAutomaticZenRule(
+ eq("another.package"), eq(rule), anyString());
+ }
+
+ @Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid);
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
index 4c69857..487390d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
@@ -228,4 +228,30 @@
mKeyCombinationManager.interceptKey(firstKeyDown, true);
assertTrue(mKeyCombinationManager.getKeyInterceptTimeout(KEYCODE_VOLUME_UP) > eventTime);
}
-}
+
+ @Test
+ public void testAddRemove() throws InterruptedException {
+ final KeyCombinationManager.TwoKeysCombinationRule rule =
+ new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN,
+ KEYCODE_POWER) {
+ @Override
+ void execute() {
+ mAction1Triggered.countDown();
+ }
+
+ @Override
+ void cancel() {
+ }
+ };
+
+ long eventTime = SystemClock.uptimeMillis();
+ mKeyCombinationManager.removeRule(rule);
+ pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
+ assertFalse(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
+
+ mKeyCombinationManager.addRule(rule);
+ eventTime = SystemClock.uptimeMillis();
+ pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
+ assertTrue(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
+ }
+}
\ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 8f94468..280afba 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -297,4 +297,23 @@
pressKey(KEYCODE_BACK, mLongPressTime);
assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
+
+ @Test
+ public void testAddRemove() throws InterruptedException {
+ final SingleKeyGestureDetector.SingleKeyRule rule =
+ new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER) {
+ @Override
+ void onPress(long downTime) {
+ mShortPressed.countDown();
+ }
+ };
+
+ mDetector.removeRule(rule);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ assertFalse(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+
+ mDetector.addRule(rule);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
}
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 25c8f14..6ed8460 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -158,7 +158,6 @@
import com.android.internal.R;
import com.android.server.wm.ActivityRecord.State;
-import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Assert;
import org.junit.Before;
@@ -552,9 +551,9 @@
final Rect insets = new Rect();
final DisplayInfo displayInfo = task.mDisplayContent.getDisplayInfo();
final DisplayPolicy policy = task.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
- displayInfo.logicalHeight, WmDisplayCutout.NO_CUTOUT, insets);
- policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
+
+ insets.set(policy.getDecorInsetsInfo(displayInfo.rotation, displayInfo.logicalWidth,
+ displayInfo.logicalHeight).mConfigInsets);
Task.intersectWithInsetsIfFits(stableRect, stableRect, insets);
final boolean isScreenPortrait = stableRect.width() <= stableRect.height();
@@ -594,9 +593,8 @@
final Rect insets = new Rect();
final DisplayInfo displayInfo = rootTask.mDisplayContent.getDisplayInfo();
final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
- displayInfo.logicalHeight, WmDisplayCutout.NO_CUTOUT, insets);
- policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
+ insets.set(policy.getDecorInsetsInfo(displayInfo.rotation, displayInfo.logicalWidth,
+ displayInfo.logicalHeight).mConfigInsets);
Task.intersectWithInsetsIfFits(stableRect, stableRect, insets);
final boolean isScreenPortrait = stableRect.width() <= stableRect.height();
@@ -2986,31 +2984,27 @@
}
@Test
- public void testCloseToSquareFixedOrientationPortrait() {
+ public void testCloseToSquareFixedOrientation() {
// create a square display
final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
.setSystemDecorations(true).build();
+ // Add a decor insets provider window.
+ final WindowState navbar = createNavBarWithProvidedInsets(squareDisplay);
+ squareDisplay.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(navbar);
final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
// create a fixed portrait activity
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
- // check that both the configuration and app bounds are portrait
+ // The available space could be landscape because of decor insets, but the configuration
+ // should still respect the requested portrait orientation.
assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
<= activity.getConfiguration().windowConfiguration.getAppBounds().height());
- }
-
- @Test
- public void testCloseToSquareFixedOrientationLandscape() {
- // create a square display
- final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
- .setSystemDecorations(true).build();
- final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
// create a fixed landscape activity
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ activity = new ActivityBuilder(mAtm).setTask(task)
.setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE).build();
// check that both the configuration and app bounds are landscape
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 cd087e6..2204ae3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -35,6 +35,7 @@
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
@@ -66,6 +67,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.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -147,6 +149,9 @@
@Before
public void setUp() throws Exception {
mController = mock(ActivityStartController.class);
+ BackgroundActivityStartController balController =
+ new BackgroundActivityStartController(mAtm, mSupervisor);
+ doReturn(balController).when(mController).getBackgroundActivityLaunchController();
mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
clearInvocations(mActivityMetricsLogger);
}
@@ -208,10 +213,13 @@
int expectedResult) {
final ActivityTaskManagerService service = mAtm;
final IPackageManager packageManager = mock(IPackageManager.class);
- final ActivityStartController controller = mock(ActivityStartController.class);
- final ActivityStarter starter = new ActivityStarter(controller, service,
- service.mTaskSupervisor, mock(ActivityStartInterceptor.class));
+ final ActivityStarter starter =
+ new ActivityStarter(
+ mController,
+ service,
+ service.mTaskSupervisor,
+ mock(ActivityStartInterceptor.class));
prepareStarter(launchFlags);
final IApplicationThread caller = mock(IApplicationThread.class);
final WindowProcessListener listener = mock(WindowProcessListener.class);
@@ -1402,6 +1410,39 @@
canEmbedActivity(taskFragment, starting, task));
}
+ @Test
+ public void testRecordActivityMovementBeforeDeliverToTop() {
+ 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();
+
+ activityBot.setVisible(false);
+ activityBot.mVisibleRequested = false;
+
+ assertTrue(activityTop.isVisible());
+ assertTrue(activityTop.mVisibleRequested);
+
+ final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_REORDER_TO_FRONT
+ | FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */);
+ starter.mStartActivity = activityBot;
+ task.inRecents = true;
+ starter.setInTask(task);
+ starter.getIntent().setComponent(activityBot.mActivityComponent);
+ final int result = starter.setReason("testRecordActivityMovement").execute();
+
+ assertEquals(START_DELIVERED_TO_TOP, result);
+ assertNotNull(starter.mMovedToTopActivity);
+
+ final ActivityStarter starter2 = prepareStarter(FLAG_ACTIVITY_REORDER_TO_FRONT
+ | FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */);
+ starter2.setInTask(task);
+ starter2.getIntent().setComponent(activityBot.mActivityComponent);
+ final int result2 = starter2.setReason("testRecordActivityMovement").execute();
+
+ assertEquals(START_DELIVERED_TO_TOP, result2);
+ assertNull(starter2.mMovedToTopActivity);
+ }
+
private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
ActivityRecord source, ActivityOptions options, Task inTask,
TaskFragment inTaskFragment) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index aa5a74e..0a59ae1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -25,13 +25,10 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
-import com.android.server.wm.utils.WmDisplayCutout;
-
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
@@ -49,8 +46,7 @@
@Test
public void portrait() {
- final Pair<DisplayInfo, WmDisplayCutout> di =
- displayInfoForRotation(ROTATION_0, false /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -59,8 +55,7 @@
@Test
public void portrait_withCutout() {
- final Pair<DisplayInfo, WmDisplayCutout> di =
- displayInfoForRotation(ROTATION_0, true /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT);
@@ -69,8 +64,7 @@
@Test
public void landscape() {
- final Pair<DisplayInfo, WmDisplayCutout> di =
- displayInfoForRotation(ROTATION_90, false /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
if (mDisplayPolicy.navigationBarCanMove()) {
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
@@ -85,8 +79,7 @@
@Test
public void landscape_withCutout() {
- final Pair<DisplayInfo, WmDisplayCutout> di =
- displayInfoForRotation(ROTATION_90, true /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
if (mDisplayPolicy.navigationBarCanMove()) {
verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
@@ -101,8 +94,7 @@
@Test
public void seascape() {
- final Pair<DisplayInfo, WmDisplayCutout> di =
- displayInfoForRotation(ROTATION_270, false /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
if (mDisplayPolicy.navigationBarCanMove()) {
verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
@@ -117,8 +109,7 @@
@Test
public void seascape_withCutout() {
- final Pair<DisplayInfo, WmDisplayCutout> di =
- displayInfoForRotation(ROTATION_270, true /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
if (mDisplayPolicy.navigationBarCanMove()) {
verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
@@ -133,8 +124,7 @@
@Test
public void upsideDown() {
- final Pair<DisplayInfo, WmDisplayCutout> di =
- displayInfoForRotation(ROTATION_180, false /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -143,34 +133,32 @@
@Test
public void upsideDown_withCutout() {
- final Pair<DisplayInfo, WmDisplayCutout> di =
- displayInfoForRotation(ROTATION_180, true /* withCutout */);
+ final DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
verifyConsistency(di);
}
- private void verifyStableInsets(Pair<DisplayInfo, WmDisplayCutout> diPair, int left, int top,
+ private void verifyStableInsets(DisplayInfo di, int left, int top,
int right, int bottom) {
- mErrorCollector.checkThat("stableInsets", getStableInsetsLw(diPair.first, diPair.second),
+ mErrorCollector.checkThat("stableInsets", getStableInsets(di),
equalTo(new Rect(left, top, right, bottom)));
}
- private void verifyNonDecorInsets(Pair<DisplayInfo, WmDisplayCutout> diPair, int left, int top,
+ private void verifyNonDecorInsets(DisplayInfo di, int left, int top,
int right, int bottom) {
mErrorCollector.checkThat("nonDecorInsets",
- getNonDecorInsetsLw(diPair.first, diPair.second), equalTo(new Rect(
- left, top, right, bottom)));
+ getNonDecorInsets(di), equalTo(new Rect(left, top, right, bottom)));
}
- private void verifyConsistency(Pair<DisplayInfo, WmDisplayCutout> diPair) {
- final DisplayInfo di = diPair.first;
- final WmDisplayCutout cutout = diPair.second;
- verifyConsistency("configDisplay", di, getStableInsetsLw(di, cutout),
- getConfigDisplayWidth(di, cutout), getConfigDisplayHeight(di, cutout));
- verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di, cutout),
- getNonDecorDisplayWidth(di, cutout), getNonDecorDisplayHeight(di, cutout));
+ private void verifyConsistency(DisplayInfo di) {
+ final DisplayPolicy.DecorInsets.Info info = mDisplayPolicy.getDecorInsetsInfo(
+ di.rotation, di.logicalWidth, di.logicalHeight);
+ verifyConsistency("configDisplay", di, info.mConfigInsets,
+ info.mConfigFrame.width(), info.mConfigFrame.height());
+ verifyConsistency("nonDecorDisplay", di, info.mNonDecorInsets,
+ info.mNonDecorFrame.width(), info.mNonDecorFrame.height());
}
private void verifyConsistency(String what, DisplayInfo di, Rect insets, int width,
@@ -181,42 +169,18 @@
equalTo(di.logicalHeight - insets.top - insets.bottom));
}
- private Rect getStableInsetsLw(DisplayInfo di, WmDisplayCutout cutout) {
- Rect result = new Rect();
- mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- cutout, result);
- return result;
+ private Rect getStableInsets(DisplayInfo di) {
+ return mDisplayPolicy.getDecorInsetsInfo(
+ di.rotation, di.logicalWidth, di.logicalHeight).mConfigInsets;
}
- private Rect getNonDecorInsetsLw(DisplayInfo di, WmDisplayCutout cutout) {
- Rect result = new Rect();
- mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- cutout, result);
- return result;
+ private Rect getNonDecorInsets(DisplayInfo di) {
+ return mDisplayPolicy.getDecorInsetsInfo(
+ di.rotation, di.logicalWidth, di.logicalHeight).mNonDecorInsets;
}
- private int getNonDecorDisplayWidth(DisplayInfo di, WmDisplayCutout cutout) {
- return mDisplayPolicy.getNonDecorDisplayFrame(di.logicalWidth, di.logicalHeight,
- di.rotation, cutout).width();
- }
-
- private int getNonDecorDisplayHeight(DisplayInfo di, WmDisplayCutout cutout) {
- return mDisplayPolicy.getNonDecorDisplayFrame(di.logicalWidth, di.logicalHeight,
- di.rotation, cutout).height();
- }
-
- private int getConfigDisplayWidth(DisplayInfo di, WmDisplayCutout cutout) {
- return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
- di.rotation, cutout).x;
- }
-
- private int getConfigDisplayHeight(DisplayInfo di, WmDisplayCutout cutout) {
- return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
- di.rotation, cutout).y;
- }
-
- private static Pair<DisplayInfo, WmDisplayCutout> displayInfoForRotation(int rotation,
- boolean withDisplayCutout) {
- return displayInfoAndCutoutForRotation(rotation, withDisplayCutout, false);
+ private DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
+ return displayInfoAndCutoutForRotation(
+ rotation, withDisplayCutout, false /* isLongEdgeCutout */);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 6bdc2e3..70b68c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -41,7 +41,6 @@
import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
import android.view.DisplayInfo;
import android.view.InsetsFrameProvider;
import android.view.InsetsState;
@@ -50,8 +49,6 @@
import androidx.test.filters.SmallTest;
-import com.android.server.wm.utils.WmDisplayCutout;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -123,7 +120,7 @@
private void updateDisplayFrames() {
mFrames = createDisplayFrames(
mDisplayContent.getInsetsStateController().getRawInsetsState());
- mDisplayBounds.set(0, 0, mFrames.mDisplayWidth, mFrames.mDisplayHeight);
+ mDisplayBounds.set(0, 0, mFrames.mWidth, mFrames.mHeight);
mDisplayContent.mDisplayFrames = mFrames;
mDisplayContent.setBounds(mDisplayBounds);
mDisplayPolicy.layoutWindowLw(mNavBarWindow, null, mFrames);
@@ -131,13 +128,13 @@
}
private DisplayFrames createDisplayFrames(InsetsState insetsState) {
- final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
+ final DisplayInfo info = displayInfoAndCutoutForRotation(mRotation,
mHasDisplayCutout, mIsLongEdgeDisplayCutout);
final RoundedCorners roundedCorners = mHasRoundedCorners
? mDisplayContent.calculateRoundedCornersForRotation(mRotation)
: RoundedCorners.NO_ROUNDED_CORNERS;
- return new DisplayFrames(mDisplayContent.getDisplayId(),
- insetsState, info.first, info.second, roundedCorners, new PrivacyIndicatorBounds());
+ return new DisplayFrames(insetsState, info,
+ info.displayCutout, roundedCorners, new PrivacyIndicatorBounds());
}
@Test
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 9cc665b..262b141 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -35,13 +36,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT;
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;
@@ -286,10 +286,18 @@
DisplayPolicy.isOverlappingWithNavBar(targetWin));
}
- private WindowState createNavigationBarWindow() {
- final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
- win.mHasSurface = true;
- return win;
+ @Test
+ public void testUpdateDisplayConfigurationByDecor() {
+ final WindowState navbar = createNavBarWithProvidedInsets(mDisplayContent);
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ final DisplayInfo di = mDisplayContent.getDisplayInfo();
+ final int prevScreenHeightDp = mDisplayContent.getConfiguration().screenHeightDp;
+ assertTrue(displayPolicy.updateDecorInsetsInfoIfNeeded(navbar));
+ assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
+ di.logicalWidth, di.logicalHeight).mConfigInsets.bottom);
+ mDisplayContent.sendNewConfiguration();
+ assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp);
+ assertFalse(displayPolicy.updateDecorInsetsInfoIfNeeded(navbar));
}
@SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
@@ -307,7 +315,7 @@
mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
mImeWindow.mAboveInsetsState.set(state);
- mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
+ mDisplayContent.mDisplayFrames = new DisplayFrames(
state, displayInfo, NO_CUTOUT, NO_ROUNDED_CORNERS, new PrivacyIndicatorBounds());
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 97b1c91..fe890d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -30,25 +30,14 @@
import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyInt;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Binder;
-import android.os.IBinder;
-import android.testing.TestableResources;
-import android.util.Pair;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.WindowManagerGlobal;
-import com.android.internal.R;
-import com.android.server.wm.utils.WmDisplayCutout;
-
import org.junit.Before;
public class DisplayPolicyTestsBase extends WindowTestsBase {
@@ -69,23 +58,8 @@
mDisplayPolicy = mDisplayContent.getDisplayPolicy();
spyOn(mDisplayPolicy);
-
- final TestContextWrapper context = new TestContextWrapper(
- mDisplayPolicy.getContext(), mDisplayPolicy.getCurrentUserResources());
- final TestableResources resources = context.getResourceMocker();
- resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
- resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
- resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
- resources.addOverride(R.dimen.navigation_bar_frame_height_landscape, NAV_BAR_HEIGHT);
- resources.addOverride(R.dimen.navigation_bar_frame_height, NAV_BAR_HEIGHT);
- doReturn(STATUS_BAR_HEIGHT).when(mDisplayPolicy).getStatusBarHeightForRotation(anyInt());
- doReturn(resources.getResources()).when(mDisplayPolicy).getCurrentUserResources();
doReturn(true).when(mDisplayPolicy).hasNavigationBar();
doReturn(true).when(mDisplayPolicy).hasStatusBar();
-
- mDisplayContent.getDisplayRotation().configure(DISPLAY_WIDTH, DISPLAY_HEIGHT);
- mDisplayPolicy.onConfigurationChanged();
-
addWindow(mStatusBarWindow);
addWindow(mNavBarWindow);
@@ -101,24 +75,20 @@
win.mHasSurface = true;
}
- static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
- boolean withDisplayCutout, boolean isLongEdgeCutout) {
- final DisplayInfo info = new DisplayInfo();
- WmDisplayCutout cutout = WmDisplayCutout.NO_CUTOUT;
-
+ DisplayInfo displayInfoAndCutoutForRotation(int rotation, boolean withDisplayCutout,
+ boolean isLongEdgeCutout) {
+ final DisplayInfo info = mDisplayContent.getDisplayInfo();
final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
info.rotation = rotation;
- if (withDisplayCutout) {
- cutout = WmDisplayCutout.computeSafeInsets(
- displayCutoutForRotation(rotation, isLongEdgeCutout), info.logicalWidth,
- info.logicalHeight);
- info.displayCutout = cutout.getDisplayCutout();
- } else {
- info.displayCutout = null;
- }
- return Pair.create(info, cutout);
+ mDisplayContent.mInitialDisplayCutout = withDisplayCutout
+ ? displayCutoutForRotation(ROTATION_0, isLongEdgeCutout)
+ : DisplayCutout.NO_CUTOUT;
+ info.displayCutout = mDisplayContent.calculateDisplayCutoutForRotation(rotation);
+ mDisplayContent.updateBaseDisplayMetrics(DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ info.logicalDensityDpi, info.physicalXDpi, info.physicalYDpi);
+ return info;
}
private static DisplayCutout displayCutoutForRotation(int rotation, boolean isLongEdgeCutout) {
@@ -152,33 +122,4 @@
return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
(int) rectF.right, (int) rectF.bottom, pos);
}
-
- static class TestContextWrapper extends ContextWrapper {
- private final TestableResources mResourceMocker;
-
- TestContextWrapper(Context targetContext, Resources targetResources) {
- super(targetContext);
- mResourceMocker = new TestableResources(targetResources);
- }
-
- @Override
- public int checkPermission(String permission, int pid, int uid) {
- return PackageManager.PERMISSION_GRANTED;
- }
-
- @Override
- public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
- return PackageManager.PERMISSION_GRANTED;
- }
-
- @Override
- public Resources getResources() {
- return mResourceMocker.getResources();
- }
-
- TestableResources getResourceMocker() {
- return mResourceMocker;
- }
- }
-
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 892b5f9..89f7111 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -20,6 +20,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
@@ -68,7 +69,6 @@
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.After;
import org.junit.AfterClass;
@@ -1008,7 +1008,7 @@
mMockDisplayContent = mock(DisplayContent.class);
mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt()))
- .thenReturn(WmDisplayCutout.NO_CUTOUT);
+ .thenReturn(NO_CUTOUT);
when(mMockDisplayContent.getDefaultTaskDisplayArea())
.thenReturn(mock(TaskDisplayArea.class));
when(mMockDisplayContent.getWindowConfiguration())
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 1738706..646f43c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -3133,8 +3133,8 @@
}
private static void resizeDisplay(DisplayContent displayContent, int width, int height) {
- displayContent.mBaseDisplayWidth = width;
- displayContent.mBaseDisplayHeight = height;
+ displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity,
+ displayContent.mBaseDisplayPhysicalXDpi, displayContent.mBaseDisplayPhysicalYDpi);
final Configuration c = new Configuration();
displayContent.computeScreenConfiguration(c);
displayContent.onRequestedOverrideConfigurationChanged(c);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 6a7e388..8e89d6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -36,7 +36,6 @@
import static android.view.Surface.ROTATION_90;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
@@ -264,7 +263,8 @@
// Detach from process so the activities can be removed from hierarchy when finishing.
activity1.detachFromProcess();
activity2.detachFromProcess();
- assertTrue(task.performClearTop(activity1, 0 /* launchFlags */).finishing);
+ int[] finishCount = new int[1];
+ assertTrue(task.performClearTop(activity1, 0 /* launchFlags */, finishCount).finishing);
assertFalse(task.hasChild());
// In real case, the task should be preserved for adding new activity.
assertTrue(task.isAttached());
@@ -278,7 +278,7 @@
doReturn(true).when(activityB).shouldBeVisibleUnchecked();
doReturn(true).when(activityC).shouldBeVisibleUnchecked();
activityA.getConfiguration().densityDpi += 100;
- assertTrue(task.performClearTop(activityA, 0 /* launchFlags */).finishing);
+ assertTrue(task.performClearTop(activityA, 0 /* launchFlags */, finishCount).finishing);
// The bottom activity should destroy directly without relaunch for config change.
assertEquals(ActivityRecord.State.DESTROYING, activityA.getState());
verify(activityA, never()).startRelaunching();
@@ -692,12 +692,9 @@
// Setup the display with a top stable inset. The later assertion will ensure the inset is
// excluded from screenHeightDp.
final int statusBarHeight = 100;
- final DisplayPolicy policy = display.getDisplayPolicy();
- doAnswer(invocationOnMock -> {
- final Rect insets = invocationOnMock.<Rect>getArgument(0);
- insets.top = statusBarHeight;
- return null;
- }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0));
+ final DisplayInfo di = display.getDisplayInfo();
+ display.getDisplayPolicy().getDecorInsetsInfo(di.rotation,
+ di.logicalWidth, di.logicalHeight).mConfigInsets.top = statusBarHeight;
// Without limiting to be inside the parent bounds, the out screen size should keep relative
// to the input bounds.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index f5304d0..aa3ca18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -18,13 +18,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
-import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
-import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
-import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
-import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -33,7 +26,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import android.annotation.Nullable;
@@ -47,7 +39,6 @@
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
-import android.view.WindowInsets;
import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
@@ -209,26 +200,6 @@
doReturn(true).when(newDisplay).supportsSystemDecorations();
doReturn(true).when(displayPolicy).hasNavigationBar();
doReturn(NAV_BAR_BOTTOM).when(displayPolicy).navigationBarPosition(anyInt());
- doReturn(Insets.of(0, 0, 0, 20)).when(displayPolicy).getInsets(any(),
- eq(WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars()));
- doReturn(Insets.of(0, 20, 0, 20)).when(displayPolicy).getInsets(any(),
- eq(WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars()
- | WindowInsets.Type.statusBars()));
- final int[] nonDecorTypes = new int[]{
- ITYPE_TOP_DISPLAY_CUTOUT, ITYPE_RIGHT_DISPLAY_CUTOUT,
- ITYPE_BOTTOM_DISPLAY_CUTOUT, ITYPE_LEFT_DISPLAY_CUTOUT, ITYPE_NAVIGATION_BAR
- };
- doReturn(Insets.of(0, 0, 0, 20)).when(displayPolicy).getInsetsWithInternalTypes(
- any(),
- eq(nonDecorTypes));
- final int[] stableTypes = new int[]{
- ITYPE_TOP_DISPLAY_CUTOUT, ITYPE_RIGHT_DISPLAY_CUTOUT,
- ITYPE_BOTTOM_DISPLAY_CUTOUT, ITYPE_LEFT_DISPLAY_CUTOUT,
- ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR, ITYPE_CLIMATE_BAR
- };
- doReturn(Insets.of(0, 20, 0, 20)).when(displayPolicy).getInsetsWithInternalTypes(
- any(),
- eq(stableTypes));
} else {
doReturn(false).when(displayPolicy).hasNavigationBar();
doReturn(false).when(displayPolicy).hasStatusBar();
@@ -241,11 +212,6 @@
displayPolicy.finishScreenTurningOn();
if (mStatusBarHeight > 0) {
doReturn(true).when(displayPolicy).hasStatusBar();
- doAnswer(invocation -> {
- Rect inOutInsets = (Rect) invocation.getArgument(0);
- inOutInsets.top = mStatusBarHeight;
- return null;
- }).when(displayPolicy).convertNonDecorInsetsToStableInsets(any(), anyInt());
}
Configuration c = new Configuration();
newDisplay.computeScreenConfiguration(c);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 003614a..048cd7c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -27,6 +27,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -83,6 +84,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@@ -965,8 +967,17 @@
@Test
public void testTransientLaunch() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
+ final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>();
final TransitionController controller = new TransitionController(mAtm, snapshotController,
- mock(TransitionTracer.class));
+ mock(TransitionTracer.class)) {
+ @Override
+ protected void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
+ if (ar.mEnteringAnimation) {
+ enteringAnimReports.add(ar);
+ }
+ super.dispatchLegacyAppTransitionFinished(ar);
+ }
+ };
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* playerProc */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -1011,6 +1022,7 @@
activity1.mVisibleRequested = false;
activity2.mVisibleRequested = true;
+ activity2.setVisible(true);
// Using abort to force-finish the sync (since we obviously can't wait for drawing).
// We didn't call abort on the actual transition, so it will still run onTransactionReady
@@ -1021,9 +1033,11 @@
// called until finish).
verify(snapshotController, times(0)).recordTaskSnapshot(eq(task1), eq(false));
+ enteringAnimReports.clear();
closeTransition.finishTransition();
verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false));
+ assertTrue(enteringAnimReports.contains(activity2));
}
@Test
@@ -1170,6 +1184,42 @@
assertTrue(mSyncEngine.isReady(transition.getSyncId()));
}
+ @Test
+ public void testVisibleChange_snapshot() {
+ registerTestTransitionPlayer();
+ final ActivityRecord app = createActivityRecord(mDisplayContent);
+ final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
+ app.mTransitionController, mWm.mSyncEngine);
+ app.mTransitionController.moveToCollecting(transition, BLASTSyncEngine.METHOD_NONE);
+ final SurfaceControl mockSnapshot = mock(SurfaceControl.class);
+ transition.setContainerFreezer(new Transition.IContainerFreezer() {
+ @Override
+ public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
+ Objects.requireNonNull(transition.mChanges.get(wc)).mSnapshot = mockSnapshot;
+ return true;
+ }
+
+ @Override
+ public void cleanUp(SurfaceControl.Transaction t) {
+ }
+ });
+ final Task task = app.getTask();
+ transition.collect(task);
+ final Rect bounds = new Rect(task.getBounds());
+ Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
+ bounds.inset(10, 10);
+ c.windowConfiguration.setBounds(bounds);
+ task.onRequestedOverrideConfigurationChanged(c);
+
+ ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ transition.mParticipants, transition.mChanges);
+ TransitionInfo info = Transition.calculateTransitionInfo(
+ TRANSIT_CHANGE, 0, targets, transition.mChanges, mMockT);
+ assertEquals(mockSnapshot,
+ info.getChange(task.mRemoteToken.toWindowContainerToken()).getSnapshot());
+ transition.abort();
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 8288713..6333508 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -52,6 +52,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsState;
@@ -63,8 +64,6 @@
import androidx.test.filters.SmallTest;
-import com.android.server.wm.utils.WmDisplayCutout;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -164,8 +163,8 @@
// Apply the fixed transform
Configuration config = new Configuration();
final DisplayInfo info = dc.computeScreenConfiguration(config, Surface.ROTATION_0);
- final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0);
- final DisplayFrames displayFrames = new DisplayFrames(dc.getDisplayId(), new InsetsState(),
+ final DisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0);
+ final DisplayFrames displayFrames = new DisplayFrames(new InsetsState(),
info, cutout, RoundedCorners.NO_ROUNDED_CORNERS, new PrivacyIndicatorBounds());
wallpaperWindow.mToken.applyFixedRotationTransform(info, displayFrames, config);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 5c7b882..e8c8054 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -671,11 +671,9 @@
verify(t, never()).setMatrix(any(), anyInt(), anyInt(), anyInt(), anyInt());
// According to "dp * density / 160 = px", density is scaled and the size in dp is the same.
- final CompatibilityInfo compatInfo = cmp.compatibilityInfoForPackageLocked(
- mContext.getApplicationInfo());
final Configuration winConfig = w.getConfiguration();
final Configuration clientConfig = new Configuration(w.getConfiguration());
- compatInfo.applyToConfiguration(clientConfig.densityDpi, clientConfig);
+ CompatibilityInfo.scaleConfiguration(w.mInvGlobalScale, clientConfig);
assertEquals(winConfig.screenWidthDp, clientConfig.screenWidthDp);
assertEquals(winConfig.screenHeightDp, clientConfig.screenHeightDp);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 6785979..353b757 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -26,6 +26,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.os.Process.SYSTEM_UID;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
@@ -69,6 +70,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
@@ -84,6 +86,7 @@
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindow;
+import android.view.InsetsFrameProvider;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsVisibilities;
@@ -419,6 +422,15 @@
true /* ownerCanManageAppTokens */);
}
+ WindowState createNavBarWithProvidedInsets(DisplayContent dc) {
+ final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, dc, "navbar");
+ navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_NAVIGATION_BAR, Insets.of(0, 0, 0, NAV_BAR_HEIGHT))
+ };
+ dc.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
+ return navbar;
+ }
+
WindowState createAppWindow(Task task, int type, String name) {
final ActivityRecord activity = createNonAttachedActivityRecord(task.getDisplayContent());
task.addChild(activity, 0);
diff --git a/services/usb/java/com/android/server/usb/PowerBoostSetter.java b/services/usb/java/com/android/server/usb/PowerBoostSetter.java
new file mode 100644
index 0000000..eb7c196
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/PowerBoostSetter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.usb;
+
+import android.os.PowerManagerInternal;
+import android.util.Log;
+
+import com.android.server.LocalServices;
+
+import java.time.Instant;
+
+/**
+ * Sends power boost events to the power manager.
+ */
+public class PowerBoostSetter {
+ private static final String TAG = "PowerBoostSetter";
+ // Set power boost timeout to 15 seconds
+ private static final int POWER_BOOST_TIMEOUT_MS = 15 * 1000;
+
+ PowerManagerInternal mPowerManagerInternal = null;
+ Instant mPreviousTimeout = null;
+
+ PowerBoostSetter() {
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ }
+
+ /**
+ * Boosts the CPU clock frequency as if the screen is touched
+ */
+ public void boostPower() {
+ if (mPowerManagerInternal == null) {
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ }
+
+ if (mPowerManagerInternal == null) {
+ Log.w(TAG, "PowerManagerInternal null");
+ } else if ((mPreviousTimeout == null) || Instant.now().isAfter(
+ mPreviousTimeout.plusMillis(POWER_BOOST_TIMEOUT_MS / 2))) {
+ // Only boost if the previous timeout is at least halfway done
+ mPreviousTimeout = Instant.now();
+ mPowerManagerInternal.setPowerBoost(PowerManagerInternal.BOOST_INTERACTION,
+ POWER_BOOST_TIMEOUT_MS);
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index eebcccb..2ae328b 100644
--- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -98,6 +98,11 @@
private UsbMidiPacketConverter mUsbMidiPacketConverter;
+ private PowerBoostSetter mPowerBoostSetter = null;
+
+ private static final byte MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE = 0x02;
+ private static final byte MESSAGE_TYPE_MIDI_2_CHANNEL_VOICE = 0x04;
+
private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
@Override
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
@@ -251,6 +256,8 @@
for (int port = 0; port < numOutputs; port++) {
mMidiInputPortReceivers[port] = new InputReceiverProxy();
}
+
+ mPowerBoostSetter = new PowerBoostSetter();
}
private int calculateDefaultMidiProtocol() {
@@ -418,6 +425,15 @@
}
outputReceivers[portFinal].send(convertedArray, 0,
convertedArray.length, timestamp);
+
+ // Boost power if there seems to be a voice message.
+ // For legacy devices, boost when message is more than size 1.
+ // For UMP devices, boost for channel voice messages.
+ if ((mPowerBoostSetter != null && convertedArray.length > 1)
+ && (!mIsUniversalMidiDevice
+ || isChannelVoiceMessage(convertedArray))) {
+ mPowerBoostSetter.boostPower();
+ }
}
}
} catch (IOException e) {
@@ -721,4 +737,10 @@
dump.end(token);
}
+
+ private boolean isChannelVoiceMessage(byte[] umpMessage) {
+ byte messageType = (byte) ((umpMessage[0] >> 4) & 0x0f);
+ return messageType == MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE
+ || messageType == MESSAGE_TYPE_MIDI_2_CHANNEL_VOICE;
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 67955e1..d9ad703 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -77,6 +77,8 @@
// only accessed from JNI code
private int mPipeFD = -1;
+ private PowerBoostSetter mPowerBoostSetter = null;
+
private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
@Override
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
@@ -167,6 +169,8 @@
for (int port = 0; port < numOutputs; port++) {
mMidiInputPortReceivers[port] = new InputReceiverProxy();
}
+
+ mPowerBoostSetter = new PowerBoostSetter();
}
private boolean openLocked() {
@@ -240,6 +244,11 @@
int count = mInputStreams[index].read(buffer);
outputReceivers[index].send(buffer, 0, count, timestamp);
+
+ // If messages are more than size 1, boost power.
+ if (mPowerBoostSetter != null && count > 1) {
+ mPowerBoostSetter.boostPower();
+ }
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 63781cc..980b3b5 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -618,10 +618,10 @@
mScheduledExecutorService.execute(() -> {
if (DEBUG) {
- Slog.d(TAG, "call updateVisibleActivitiesLocked from enable listening");
+ Slog.d(TAG, "call handleVisibleActivitiesLocked from enable listening");
}
synchronized (mLock) {
- updateVisibleActivitiesLocked();
+ handleVisibleActivitiesLocked();
}
});
}
@@ -646,10 +646,10 @@
}
mScheduledExecutorService.execute(() -> {
if (DEBUG) {
- Slog.d(TAG, "call updateVisibleActivitiesLocked from activity event");
+ Slog.d(TAG, "call handleVisibleActivitiesLocked from activity event");
}
synchronized (mLock) {
- updateVisibleActivitiesLocked();
+ handleVisibleActivitiesLocked();
}
});
}
@@ -684,9 +684,9 @@
return visibleActivityInfos;
}
- private void updateVisibleActivitiesLocked() {
+ private void handleVisibleActivitiesLocked() {
if (DEBUG) {
- Slog.d(TAG, "updateVisibleActivitiesLocked");
+ Slog.d(TAG, "handleVisibleActivitiesLocked");
}
if (mSession == null) {
return;
@@ -697,13 +697,13 @@
final List<VisibleActivityInfo> newVisibleActivityInfos = getVisibleActivityInfosLocked();
if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
- updateVisibleActivitiesChangedLocked(mVisibleActivityInfos,
+ notifyVisibleActivitiesChangedLocked(mVisibleActivityInfos,
VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
mVisibleActivityInfos.clear();
return;
}
if (mVisibleActivityInfos.isEmpty()) {
- updateVisibleActivitiesChangedLocked(newVisibleActivityInfos,
+ notifyVisibleActivitiesChangedLocked(newVisibleActivityInfos,
VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
mVisibleActivityInfos.addAll(newVisibleActivityInfos);
return;
@@ -724,11 +724,11 @@
}
if (!addedActivities.isEmpty()) {
- updateVisibleActivitiesChangedLocked(addedActivities,
+ notifyVisibleActivitiesChangedLocked(addedActivities,
VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
}
if (!removedActivities.isEmpty()) {
- updateVisibleActivitiesChangedLocked(removedActivities,
+ notifyVisibleActivitiesChangedLocked(removedActivities,
VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
}
@@ -736,7 +736,7 @@
mVisibleActivityInfos.addAll(newVisibleActivityInfos);
}
- private void updateVisibleActivitiesChangedLocked(
+ private void notifyVisibleActivitiesChangedLocked(
List<VisibleActivityInfo> visibleActivityInfos, int type) {
if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) {
return;
@@ -746,15 +746,15 @@
}
try {
for (int i = 0; i < visibleActivityInfos.size(); i++) {
- mSession.updateVisibleActivityInfo(visibleActivityInfos.get(i), type);
+ mSession.notifyVisibleActivityInfoChanged(visibleActivityInfos.get(i), type);
}
} catch (RemoteException e) {
if (DEBUG) {
- Slog.w(TAG, "updateVisibleActivitiesChangedLocked RemoteException : " + e);
+ Slog.w(TAG, "notifyVisibleActivitiesChangedLocked RemoteException : " + e);
}
}
if (DEBUG) {
- Slog.d(TAG, "updateVisibleActivitiesChangedLocked type=" + type + ", count="
+ Slog.d(TAG, "notifyVisibleActivitiesChangedLocked type=" + type + ", count="
+ visibleActivityInfos.size());
}
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 49ad585..7c736e3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2763,6 +2763,12 @@
* @param isVoip True if the audio mode is VOIP.
*/
public final void setAudioModeIsVoip(boolean isVoip) {
+ if (!isVoip && (mConnectionProperties & PROPERTY_SELF_MANAGED) == PROPERTY_SELF_MANAGED) {
+ Log.i(this,
+ "setAudioModeIsVoip: Ignored request to set a self-managed connection's"
+ + " audioModeIsVoip to false. Doing so can cause unwanted behavior.");
+ return;
+ }
checkImmutable();
mAudioModeIsVoip = isVoip;
for (Listener l : mListeners) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f7d141b..3023ec9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -579,17 +579,16 @@
* List of network type constants which support only a single data connection at a time.
* Some carriers do not support multiple PDP on UMTS.
* @see TelephonyManager NETWORK_TYPE_*
+ * @see #KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY
*/
public static final String
KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
/**
- * List of network capabilities which, if requested, will exempt the request from single PDN
- * connection checks.
+ * Only apply if {@link #KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY} specifies the network types that
+ * support a single data connection at a time. This key defines a list of network capabilities
+ * which, if requested, will exempt the request from single data connection checks.
* @see NetworkCapabilities NET_CAPABILITY_*
- * @see #KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY
- *
- * @hide
*/
public static final String KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY =
"capabilities_exempt_from_single_dc_check_int_array";
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index e882b25..23835a7 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -987,8 +987,15 @@
/** The ePDG does not support un-authenticated IMSI based emergency PDN bringup **/
public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 0x2B2F;
- // Device is unable to establish an IPSec tunnel with the ePDG for any reason
- // e.g authentication fail or certificate validation or DNS Resolution and timeout failure.
+ // The below error causes are relevant when the device is unable to establish an IPSec tunnel
+ // with the ePDG for any reason, e.g. authentication fail or certificate validation or DNS
+ // Resolution and timeout failure.
+
+ /**
+ * The requested service was rejected because of congestion in the network while accessing the
+ * IWLAN ePDG. Defined in 3GPP TS 24.502, Section 9.2.4.2.
+ */
+ public static final int IWLAN_CONGESTION = 0x3C8C;
/** IKE configuration error resulting in failure */
public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index aa8011b..b4244dd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6893,11 +6893,11 @@
*
* @hide
*/
- public void setCellInfoListRate(int rateInMillis) {
+ public void setCellInfoListRate(int rateInMillis, int subId) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- telephony.setCellInfoListRate(rateInMillis);
+ telephony.setCellInfoListRate(rateInMillis, subId);
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index f794a79..6df9f9b 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1109,6 +1109,7 @@
sb.append(", ").append(mCarrierId);
sb.append(", ").append(mSkip464Xlat);
sb.append(", ").append(mAlwaysOn);
+ sb.append(", ").append(Objects.hash(mUser, mPassword));
return sb.toString();
}
@@ -1297,8 +1298,6 @@
other.mLingeringNetworkTypeBitmask)
&& Objects.equals(this.mProfileId, other.mProfileId)
&& Objects.equals(this.mPersistent, other.mPersistent)
- && Objects.equals(this.mMvnoType, other.mMvnoType)
- && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
&& Objects.equals(this.mApnSetId, other.mApnSetId)
&& Objects.equals(this.mCarrierId, other.mCarrierId)
&& Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 42cac66..3032bab 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -621,7 +621,7 @@
/**
* Sets minimum time in milli-seconds between onCellInfoChanged
*/
- void setCellInfoListRate(int rateInMillis);
+ void setCellInfoListRate(int rateInMillis, int subId);
/**
* Opens a logical channel to the ICC card.
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 f85bad3..aaace59 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
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
@@ -266,7 +265,7 @@
}
/** {@inheritDoc} */
- @Postsubmit
+ @Presubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
index 461bae4..49dcbcf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -57,29 +56,6 @@
Assume.assumeTrue(isShellTransitionsEnabled)
}
- @FlakyTest(bugId = 228009808)
- @Test
- override fun app1LayerIsVisibleOnceApp2LayerIsInvisible() =
- super.app1LayerIsVisibleOnceApp2LayerIsInvisible()
-
- @FlakyTest(bugId = 228009808)
- @Test
- override fun app1WindowBecomesAndStaysVisible() = super.app1WindowBecomesAndStaysVisible()
-
- @FlakyTest(bugId = 228009808)
- @Test
- override fun endsWithApp1BeingOnTop() = super.endsWithApp1BeingOnTop()
-
- @FlakyTest(bugId = 239147075)
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 239147075)
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
/** {@inheritDoc} */
@Ignore("Nav bar window becomes invisible during quick switch")
@Test
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 f6392ca..bb656e8 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
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
@@ -274,22 +272,10 @@
}
/** {@inheritDoc} */
- @Postsubmit
+ @Presubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
- /** {@inheritDoc} */
- @FlakyTest(bugId = 239148258)
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- /** {@inheritDoc} */
- @FlakyTest(bugId = 239148258)
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
companion object {
private var startDisplayBounds = Rect.EMPTY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
index f644b97..7c7be89 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -57,19 +56,6 @@
Assume.assumeTrue(isShellTransitionsEnabled)
}
- @FlakyTest(bugId = 228009808)
- @Test
- override fun app2LayerIsVisibleOnceApp1LayerIsInvisible() =
- super.app2LayerIsVisibleOnceApp1LayerIsInvisible()
-
- @FlakyTest(bugId = 228009808)
- @Test
- override fun app2WindowBecomesAndStaysVisible() = super.app2WindowBecomesAndStaysVisible()
-
- @FlakyTest(bugId = 228009808)
- @Test
- override fun endsWithApp2BeingOnTop() = super.endsWithApp2BeingOnTop()
-
/** {@inheritDoc} */
@Ignore("Nav bar window becomes invisible during quick switch")
@Test
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 a714111..13c9e2b 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
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
@@ -284,22 +282,11 @@
}
/** {@inheritDoc} */
- @Postsubmit
+ @Presubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
- @FlakyTest(bugId = 239148258)
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 239148258)
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- /** {@inheritDoc} */
@Ignore("Nav bar window becomes invisible during quick switch")
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp
index 0c698ab..9b1262d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/Android.bp
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp
@@ -27,7 +27,19 @@
sdk_version: "current",
test_suites: ["device-tests"],
static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.appcompat_appcompat",
+ "androidx-constraintlayout_constraintlayout",
+ "androidx.core_core",
+ "androidx.fragment_fragment",
+ "androidx.recyclerview_recyclerview",
"androidx.test.ext.junit",
+ "androidx.navigation_navigation-common-ktx",
+ "androidx.navigation_navigation-fragment-ktx",
+ "androidx.navigation_navigation-runtime-ktx",
+ "androidx.navigation_navigation-ui-ktx",
+ "kotlin-stdlib",
+ "kotlinx-coroutines-android",
"wm-flicker-common-app-helpers",
"wm-flicker-window-extensions",
],
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 387f19b..61aadaac 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -165,7 +165,6 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
-
<activity
android:name=".ActivityEmbeddingMainActivity"
android:label="ActivityEmbedding Main"
@@ -193,5 +192,15 @@
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false"/>
+ <activity android:name=".MailActivity"
+ android:exported="true"
+ android:label="MailApp"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
+ android:theme="@style/Theme.AppCompat.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/anim/slide_in_from_bottom.xml b/tests/FlickerTests/test-apps/flickerapp/res/anim/slide_in_from_bottom.xml
new file mode 100644
index 0000000..c0165e5
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/anim/slide_in_from_bottom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/linear_interpolator"
+ android:shareInterpolator="false">
+ <translate
+ android:duration="500"
+ android:fromYDelta="100%p"
+ android:toYDelta="0%p" />
+</set>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml
new file mode 100644
index 0000000..8a97433
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment
+ android:id="@+id/main_fragment"
+ android:name="androidx.navigation.fragment.NavHostFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:defaultNavHost="true"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:navGraph="@navigation/mail_navigation"></fragment>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_content.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_content.xml
new file mode 100644
index 0000000..fbbdc5b
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_content.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_list.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_list.xml
new file mode 100644
index 0000000..0e30745
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_list.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/mail_recycle_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/mail_row_item.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/mail_row_item.xml
new file mode 100644
index 0000000..c39bfb3
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/mail_row_item.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/list_item_height"
+ android:layout_marginLeft="@dimen/margin_medium"
+ android:layout_marginRight="@dimen/margin_medium">
+ <ImageView
+ android:id="@+id/mail_row_item_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/padding_small"
+ android:paddingEnd="@dimen/padding_small"
+ android:paddingStart="@dimen/padding_small"
+ android:paddingTop="@dimen/padding_small"/>
+ <TextView
+ android:id="@+id/mail_row_item_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingEnd="@dimen/padding_small"
+ android:textSize="@dimen/list_item_text_size" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/navigation/mail_navigation.xml b/tests/FlickerTests/test-apps/flickerapp/res/navigation/mail_navigation.xml
new file mode 100644
index 0000000..d12707a
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/navigation/mail_navigation.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/mail_navigation"
+ app:startDestination="@id/fragment_mail_list">
+ <fragment
+ android:id="@+id/fragment_mail_list"
+ android:name=".MailListFragment"
+ android:label="Mail List">
+ <action
+ android:id="@+id/action_mail_list_to_mail_content"
+ app:destination="@id/fragment_mail_content"
+ app:enterAnim="@anim/slide_in_from_bottom" />
+ </fragment>
+ <fragment
+ android:id="@+id/fragment_mail_content"
+ android:name=".MailContentFragment"
+ android:label="Mail Content">
+ </fragment>
+</navigation>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/dimens.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/dimens.xml
new file mode 100644
index 0000000..0b0763d
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/dimens.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <dimen name="padding_small">8dp</dimen>
+ <dimen name="margin_medium">16dp</dimen>
+ <dimen name="list_item_height">72dp</dimen>
+ <dimen name="list_item_text_size">24dp</dimen>
+ <dimen name="icon_size">64dp</dimen>
+ <dimen name="icon_text_size">36dp</dimen>
+</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailActivity.java
similarity index 60%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
copy to tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailActivity.java
index e62a63a..419d438 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailActivity.java
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode;
+package com.android.server.wm.flicker.testapp;
-import android.os.SystemProperties;
+import android.os.Bundle;
-/**
- * Constants for desktop mode feature
- */
-public class DesktopModeConstants {
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+public class MailActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_mail);
+ }
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailAdapter.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailAdapter.java
new file mode 100644
index 0000000..984263a
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailAdapter.java
@@ -0,0 +1,134 @@
+/*
+ * 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.flicker.testapp;
+
+import static androidx.navigation.fragment.NavHostFragment.findNavController;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.OvalShape;
+import android.graphics.drawable.shapes.Shape;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.navigation.NavController;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class MailAdapter extends RecyclerView.Adapter<MailAdapter.ViewHolder> {
+
+ private final Fragment mFragment;
+ private final int mSize;
+
+ static class BadgeShape extends OvalShape {
+ Context mContext;
+ String mLabel;
+
+ BadgeShape(Context context, String label) {
+ mContext = context;
+ mLabel = label;
+ }
+
+ @Override
+ public void draw(Canvas canvas, Paint paint) {
+ final Resources resources = mContext.getResources();
+ int textSize = resources.getDimensionPixelSize(R.dimen.icon_text_size);
+ paint.setColor(Color.BLACK);
+ super.draw(canvas, paint);
+ paint.setColor(Color.WHITE);
+ paint.setTextSize(textSize);
+ paint.setTypeface(Typeface.DEFAULT_BOLD);
+ paint.setTextAlign(Paint.Align.CENTER);
+ Paint.FontMetrics fontMetrics = paint.getFontMetrics();
+ float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
+ canvas.drawText(
+ mLabel,
+ rect().centerX(),
+ rect().centerY() + distance,
+ paint);
+ }
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+
+ NavController mNavController;
+ ImageView mImageView;
+ TextView mTextView;
+ DisplayMetrics mDisplayMetrics;
+
+ ViewHolder(@NonNull View itemView, NavController navController) {
+ super(itemView);
+ mNavController = navController;
+ itemView.setOnClickListener(this::onClick);
+ mImageView = itemView.findViewById(R.id.mail_row_item_icon);
+ mTextView = itemView.findViewById(R.id.mail_row_item_text);
+ mDisplayMetrics = itemView.getContext().getResources().getDisplayMetrics();
+ }
+
+ void onClick(View v) {
+ mNavController.navigate(R.id.action_mail_list_to_mail_content);
+ }
+
+ public void setContent(int i) {
+ final Resources resources = mImageView.getContext().getResources();
+ final int badgeSize = resources.getDimensionPixelSize(R.dimen.icon_size);
+ final char c = (char) ('A' + i % 26);
+ final Shape badge = new BadgeShape(mImageView.getContext(), String.valueOf(c));
+ ShapeDrawable drawable = new ShapeDrawable();
+ drawable.setIntrinsicHeight(badgeSize);
+ drawable.setIntrinsicWidth(badgeSize);
+ drawable.setShape(badge);
+ mImageView.setImageDrawable(drawable);
+ mTextView.setText(String.format("%s-%04d", c, i));
+ }
+ }
+
+ public MailAdapter(Fragment fragment, int size) {
+ super();
+ mFragment = fragment;
+ mSize = size;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
+ View v = LayoutInflater
+ .from(viewGroup.getContext())
+ .inflate(R.layout.mail_row_item, viewGroup, false);
+ return new ViewHolder(v, findNavController(mFragment));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
+ viewHolder.setContent(i);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSize;
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailContentFragment.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailContentFragment.java
new file mode 100644
index 0000000..278b92e
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailContentFragment.java
@@ -0,0 +1,36 @@
+/*
+ * 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.flicker.testapp;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+public class MailContentFragment extends Fragment {
+ public MailContentFragment() {
+ super(R.layout.fragment_mail_content);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ view.setBackgroundColor(Color.LTGRAY);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailListFragment.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailListFragment.java
new file mode 100644
index 0000000..551820c
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailListFragment.java
@@ -0,0 +1,49 @@
+/*
+ * 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.flicker.testapp;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class MailListFragment extends Fragment {
+
+ protected RecyclerView mRecyclerView;
+ protected RecyclerView.LayoutManager mLayoutManager;
+ protected MailAdapter mAdapter;
+
+ public MailListFragment() {
+ super(R.layout.fragment_mail_list);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_mail_list, container, false);
+ mRecyclerView = rootView.findViewById(R.id.mail_recycle_view);
+ mAdapter = new MailAdapter(this, 1000);
+ mRecyclerView.setAdapter(mAdapter);
+ mLayoutManager = new LinearLayoutManager(getActivity());
+ mRecyclerView.setLayoutManager(mLayoutManager);
+ return rootView;
+ }
+}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b0ccbd1..939c7de 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -436,6 +436,15 @@
</intent-filter>
</activity>
+ <activity android:name="SurfaceViewAlphaActivity"
+ android:label="SurfaceView/SurfaceView with Alpha"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name=".PenStylusActivity"
android:label="Pen/Draw"
android:exported="true">
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
new file mode 100644
index 0000000..01fe6ae0
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class SurfaceViewAlphaActivity extends Activity implements Callback {
+ SurfaceView mSurfaceView;
+
+ private enum ZOrder {
+ ABOVE,
+ BELOW
+ }
+
+ private float mAlpha = 127f / 255f;
+ private ZOrder mZOrder = ZOrder.BELOW;
+
+
+ private String getAlphaText() {
+ return "Alpha: " + mAlpha;
+ }
+
+ private void toggleZOrder() {
+ if (ZOrder.ABOVE.equals(mZOrder)) {
+ mZOrder = ZOrder.BELOW;
+ } else {
+ mZOrder = ZOrder.ABOVE;
+ }
+ }
+
+ // Overlaps a blue view on the left, then the SurfaceView in the center, then a blue view on the
+ // right.
+ private void overlapViews(SurfaceView view, LinearLayout parent) {
+ float density = getResources().getDisplayMetrics().density;
+ int surfaceViewSize = (int) (200 * density);
+ int blueViewSize = (int) (surfaceViewSize * 2 / 3f);
+ int totalSize = (int) (surfaceViewSize * 5 / 3f);
+
+ RelativeLayout overlapLayout = new RelativeLayout(this);
+
+ RelativeLayout.LayoutParams leftViewLayoutParams = new RelativeLayout.LayoutParams(
+ blueViewSize, surfaceViewSize);
+ leftViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+
+ View leftBlueView = new View(this);
+ leftBlueView.setBackgroundColor(Color.BLUE);
+ overlapLayout.addView(leftBlueView, leftViewLayoutParams);
+
+ RelativeLayout.LayoutParams sVLayoutParams = new RelativeLayout.LayoutParams(
+ surfaceViewSize, surfaceViewSize);
+ sVLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
+ overlapLayout.addView(view, sVLayoutParams);
+
+ RelativeLayout.LayoutParams rightViewLayoutParams = new RelativeLayout.LayoutParams(
+ blueViewSize, surfaceViewSize);
+ rightViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+
+ View rightBlueView = new View(this);
+ rightBlueView.setBackgroundColor(Color.BLUE);
+ overlapLayout.addView(rightBlueView, rightViewLayoutParams);
+
+ parent.addView(overlapLayout, new LinearLayout.LayoutParams(
+ totalSize, surfaceViewSize));
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.getHolder().addCallback(this);
+ mSurfaceView.setAlpha(mAlpha);
+
+ LinearLayout content = new LinearLayout(this);
+ content.setOrientation(LinearLayout.VERTICAL);
+
+ TextView alphaText = new TextView(this);
+ alphaText.setText(getAlphaText());
+
+ SeekBar alphaToggle = new SeekBar(this);
+ alphaToggle.setMin(0);
+ alphaToggle.setMax(255);
+ alphaToggle.setProgress(Math.round(mAlpha * 255));
+ alphaToggle.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mAlpha = progress / 255f;
+ alphaText.setText(getAlphaText());
+ mSurfaceView.setAlpha(mAlpha);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ content.addView(alphaText, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ content.addView(alphaToggle, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ Button button = new Button(this);
+ button.setText("Z " + mZOrder.toString());
+ button.setOnClickListener(v -> {
+ toggleZOrder();
+ mSurfaceView.setZOrderOnTop(ZOrder.ABOVE.equals(mZOrder));
+ button.setText("Z " + mZOrder.toString());
+ });
+
+ content.addView(button, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ overlapViews(mSurfaceView, content);
+
+ setContentView(content);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.RED);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
index 47f87d6..573b3b69 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -102,6 +102,7 @@
@After
public void tearDown() {
mNotificationManager.cancelAll();
+ mUiDevice.pressHome();
}
@Test
diff --git a/tools/aapt2/cmd/Dump_test.cpp b/tools/aapt2/cmd/Dump_test.cpp
index 03da364..b1c69cd 100644
--- a/tools/aapt2/cmd/Dump_test.cpp
+++ b/tools/aapt2/cmd/Dump_test.cpp
@@ -59,6 +59,22 @@
ASSERT_EQ(output, expected);
}
+TEST_F(DumpTest, DumpBadgingMultipleUsesSdkTakesLatest) {
+ auto apk_path = file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests",
+ "DumpTest", "multiple_uses_sdk.apk"});
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
+
+ std::string output;
+ DumpBadgingToString(loaded_apk.get(), &output);
+
+ std::string expected;
+ auto expected_path =
+ file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "DumpTest",
+ "multiple_uses_sdk_expected.txt"});
+ ::android::base::ReadFileToString(expected_path, &expected);
+ ASSERT_EQ(output, expected);
+}
+
TEST_F(DumpTest, DumpBadgingAllComponents) {
auto apk_path = file::BuildPath(
{android::base::GetExecutableDirectory(), "integration-tests", "DumpTest", "components.apk"});
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index b3165d3..c4c002d 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -448,7 +448,12 @@
/** Recursively visit the xml element tree and return a processed badging element tree. */
std::unique_ptr<Element> Visit(xml::Element* element);
- /** Raises the target sdk value if the min target is greater than the current target. */
+ /** Resets target SDK to 0. */
+ void ResetTargetSdk() {
+ target_sdk_ = 0;
+ }
+
+ /** Raises the target sdk value if the min target is greater than the current target. */
void RaiseTargetSdk(int32_t min_target) {
if (min_target > target_sdk_) {
target_sdk_ = min_target;
@@ -799,6 +804,10 @@
target_sdk = GetAttributeInteger(FindAttribute(element, TARGET_SDK_VERSION_ATTR));
target_sdk_name = GetAttributeString(FindAttribute(element, TARGET_SDK_VERSION_ATTR));
+ // Resets target SDK first. This is required if APK contains multiple <uses-sdk> elements,
+ // we only need to take the latest values.
+ extractor()->ResetTargetSdk();
+
// Detect the target sdk of the element
if ((min_sdk_name && *min_sdk_name == "Donut")
|| (target_sdk_name && *target_sdk_name == "Donut")) {
@@ -2845,7 +2854,9 @@
supports_screen_->ToProtoScreens(out_badging, target_sdk_);
for (auto& config : locales_) {
- if (!config.first.empty()) {
+ if (config.first.empty()) {
+ out_badging->add_locales("--_--");
+ } else {
out_badging->add_locales(config.first);
}
}
diff --git a/tools/aapt2/integration-tests/DumpTest/components_expected_proto.txt b/tools/aapt2/integration-tests/DumpTest/components_expected_proto.txt
index 7756410..4aed4af 100644
--- a/tools/aapt2/integration-tests/DumpTest/components_expected_proto.txt
+++ b/tools/aapt2/integration-tests/DumpTest/components_expected_proto.txt
@@ -94,6 +94,7 @@
provided_components: "camera"
provided_components: "camera-secure"
}
+ locales: "--_--"
densities: 160
densities: 240
densities: 320
diff --git a/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt b/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
index 8e733a5..c783f47 100644
--- a/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
+++ b/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
@@ -94,6 +94,7 @@
provided_components: "camera"
provided_components: "camera-secure"
}
+ locales: "--_--"
densities: 160
densities: 240
densities: 320
diff --git a/tools/aapt2/integration-tests/DumpTest/multiple_uses_sdk.apk b/tools/aapt2/integration-tests/DumpTest/multiple_uses_sdk.apk
new file mode 100644
index 0000000..7b269a5
--- /dev/null
+++ b/tools/aapt2/integration-tests/DumpTest/multiple_uses_sdk.apk
Binary files differ
diff --git a/tools/aapt2/integration-tests/DumpTest/multiple_uses_sdk_expected.txt b/tools/aapt2/integration-tests/DumpTest/multiple_uses_sdk_expected.txt
new file mode 100644
index 0000000..85e8d0a
--- /dev/null
+++ b/tools/aapt2/integration-tests/DumpTest/multiple_uses_sdk_expected.txt
@@ -0,0 +1,23 @@
+package: name='com.test.e17wmultiapknexus' versionCode='107' versionName='14' platformBuildVersionName='2.3.3' platformBuildVersionCode='10'
+sdkVersion:'1'
+application-label:'w45wmultiapknexus_10'
+application-icon-120:'res/drawable-ldpi-v4/icon.png'
+application-icon-160:'res/drawable-mdpi-v4/icon.png'
+application-icon-240:'res/drawable-hdpi-v4/icon.png'
+application: label='w45wmultiapknexus_10' icon='res/drawable-mdpi-v4/icon.png'
+launchable-activity: name='com.test.e17wmultiapknexus.TestActivity' label='' icon=''
+compatible-screens:'500/320'
+uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
+uses-implied-permission: name='android.permission.WRITE_EXTERNAL_STORAGE' reason='targetSdkVersion < 4'
+uses-permission: name='android.permission.READ_PHONE_STATE'
+uses-implied-permission: name='android.permission.READ_PHONE_STATE' reason='targetSdkVersion < 4'
+uses-permission: name='android.permission.READ_EXTERNAL_STORAGE'
+uses-implied-permission: name='android.permission.READ_EXTERNAL_STORAGE' reason='requested WRITE_EXTERNAL_STORAGE'
+feature-group: label=''
+ uses-feature: name='android.hardware.faketouch'
+ uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps'
+main
+supports-screens: 'small' 'normal' 'large' 'xlarge'
+supports-any-density: 'true'
+locales: '--_--'
+densities: '120' '160' '240'
diff --git a/tools/preload-check/AndroidTest.xml b/tools/preload-check/AndroidTest.xml
index a0645d5..d486f0f 100644
--- a/tools/preload-check/AndroidTest.xml
+++ b/tools/preload-check/AndroidTest.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<configuration description="Config for PreloadCheck">
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="preload-check-device.jar->/data/local/tmp/preload-check-device.jar" />
</target_preparer>
diff --git a/tools/preload-check/OWNERS b/tools/preload-check/OWNERS
new file mode 100644
index 0000000..e71c733
--- /dev/null
+++ b/tools/preload-check/OWNERS
@@ -0,0 +1 @@
+include /ZYGOTE_OWNERS