Merge "BluetoothMidiService: target SDK 30" into sc-v2-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a4fb2c1..5c9da0bc 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3204,12 +3204,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR;
}
- public final class TaskFragmentAppearedInfo implements android.os.Parcelable {
- method @NonNull public android.view.SurfaceControl getLeash();
- method @NonNull public android.window.TaskFragmentInfo getTaskFragmentInfo();
- field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentAppearedInfo> CREATOR;
- }
-
public final class TaskFragmentCreationParams implements android.os.Parcelable {
method @NonNull public android.os.IBinder getFragmentToken();
method @NonNull public android.graphics.Rect getInitialBounds();
@@ -3246,7 +3240,7 @@
ctor public TaskFragmentOrganizer(@NonNull java.util.concurrent.Executor);
method @NonNull public java.util.concurrent.Executor getExecutor();
method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizerToken();
- method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentAppearedInfo);
+ method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentInfo);
method public void onTaskFragmentError(@NonNull android.os.IBinder, @NonNull Throwable);
method public void onTaskFragmentInfoChanged(@NonNull android.window.TaskFragmentInfo);
method public void onTaskFragmentParentInfoChanged(@NonNull android.os.IBinder, @NonNull android.content.res.Configuration);
@@ -3295,7 +3289,7 @@
method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean);
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
- method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
+ method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @Nullable android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams);
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 8062832..7e38287 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -518,7 +518,9 @@
public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
/**
- * Action to toggle docking the current app's window
+ * Action to toggle docking the current app's window.
+ * <p>
+ * <strong>Note:</strong> It is effective only if it appears in {@link #getSystemActions()}.
*/
public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index db7ab1a..eb4a355 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
@@ -498,6 +499,28 @@
}
}
+ /**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
+ *
+ * @param token The token for the window that needs a control.
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ void requestCompatCameraControl(Resources res, IBinder token, boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ if (!res.getBoolean(com.android.internal.R.bool
+ .config_isCameraCompatControlForStretchedIssuesEnabled)) {
+ return;
+ }
+ try {
+ getActivityClientController().requestCompatCameraControl(
+ token, showControl, transformationApplied, callback);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
public static ActivityClient getInstance() {
return sInstance.get();
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 992f02c..db45466 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4076,6 +4076,34 @@
}
/**
+ * Gets the message that is shown when a user is switched from.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public @Nullable String getSwitchingFromUserMessage() {
+ try {
+ return getService().getSwitchingFromUserMessage();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the message that is shown when a user is switched to.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public @Nullable String getSwitchingToUserMessage() {
+ try {
+ return getService().getSwitchingToUserMessage();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Uses the value defined by the platform.
*
* @hide
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index edcab29..0ff9f66 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -431,7 +431,7 @@
private boolean mOverrideTaskTransition;
private String mSplashScreenThemeResName;
@SplashScreen.SplashScreenStyle
- private int mSplashScreenStyle;
+ private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
private boolean mRemoveWithTaskOrganizer;
private boolean mLaunchedFromBubble;
private boolean mTransientLaunch;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 17bb797..57ad989 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -29,6 +29,7 @@
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
@@ -320,7 +321,8 @@
@UnsupportedAppUsage
private ContextImpl mSystemContext;
- private ContextImpl mSystemUiContext;
+ @GuardedBy("this")
+ private SparseArray<ContextImpl> mDisplaySystemUiContexts;
@UnsupportedAppUsage
static volatile IPackageManager sPackageManager;
@@ -559,8 +561,8 @@
private Configuration mPendingOverrideConfig;
// Used for consolidating configs before sending on to Activity.
private Configuration tmpConfig = new Configuration();
- // Callback used for updating activity override config.
- ViewRootImpl.ActivityConfigCallback configCallback;
+ // Callback used for updating activity override config and camera compat control state.
+ ViewRootImpl.ActivityConfigCallback activityConfigCallback;
ActivityClientRecord nextIdle;
// Indicates whether this activity is currently the topmost resumed one in the system.
@@ -658,13 +660,30 @@
stopped = false;
hideForNow = false;
nextIdle = null;
- configCallback = (Configuration overrideConfig, int newDisplayId) -> {
- if (activity == null) {
- throw new IllegalStateException(
- "Received config update for non-existing activity");
+ activityConfigCallback = new ViewRootImpl.ActivityConfigCallback() {
+ @Override
+ public void onConfigurationChanged(Configuration overrideConfig,
+ int newDisplayId) {
+ if (activity == null) {
+ throw new IllegalStateException(
+ "Received config update for non-existing activity");
+ }
+ activity.mMainThread.handleActivityConfigurationChanged(
+ ActivityClientRecord.this, overrideConfig, newDisplayId);
}
- activity.mMainThread.handleActivityConfigurationChanged(this, overrideConfig,
- newDisplayId);
+
+ @Override
+ public void requestCompatCameraControl(boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ if (activity == null) {
+ throw new IllegalStateException(
+ "Received camera compat control update for non-existing activity");
+ }
+ ActivityClient.getInstance().requestCompatCameraControl(
+ activity.getResources(), token, showControl, transformationApplied,
+ callback);
+ }
+
};
}
@@ -1293,8 +1312,11 @@
}
@Override
- public void scheduleCrash(String msg, int typeId) {
- sendMessage(H.SCHEDULE_CRASH, msg, typeId);
+ public void scheduleCrash(String msg, int typeId, @Nullable Bundle extras) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = msg;
+ args.arg2 = extras;
+ sendMessage(H.SCHEDULE_CRASH, args, typeId);
}
public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken,
@@ -1924,11 +1946,11 @@
}
}
- private void throwRemoteServiceException(String message, int typeId) {
+ private void throwRemoteServiceException(String message, int typeId, @Nullable Bundle extras) {
// Use a switch to ensure all the type IDs are unique.
switch (typeId) {
case ForegroundServiceDidNotStartInTimeException.TYPE_ID:
- throw new ForegroundServiceDidNotStartInTimeException(message);
+ throw generateForegroundServiceDidNotStartInTimeException(message, extras);
case CannotDeliverBroadcastException.TYPE_ID:
throw new CannotDeliverBroadcastException(message);
@@ -1951,6 +1973,15 @@
}
}
+ private ForegroundServiceDidNotStartInTimeException
+ generateForegroundServiceDidNotStartInTimeException(String message, Bundle extras) {
+ final String serviceClassName =
+ ForegroundServiceDidNotStartInTimeException.getServiceClassNameFromExtras(extras);
+ final Exception inner = (serviceClassName == null) ? null
+ : Service.getStartForegroundServiceStackTrace(serviceClassName);
+ throw new ForegroundServiceDidNotStartInTimeException(message, inner);
+ }
+
class H extends Handler {
public static final int BIND_APPLICATION = 110;
@UnsupportedAppUsage
@@ -2168,9 +2199,14 @@
handleDispatchPackageBroadcast(msg.arg1, (String[])msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
- case SCHEDULE_CRASH:
- throwRemoteServiceException((String) msg.obj, msg.arg1);
+ case SCHEDULE_CRASH: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String message = (String) args.arg1;
+ Bundle extras = (Bundle) args.arg2;
+ args.recycle();
+ throwRemoteServiceException(message, msg.arg1, extras);
break;
+ }
case DUMP_HEAP:
handleDumpHeap((DumpHeapData) msg.obj);
break;
@@ -2632,23 +2668,48 @@
}
}
- @Override
+ @NonNull
public ContextImpl getSystemUiContext() {
- synchronized (this) {
- if (mSystemUiContext == null) {
- mSystemUiContext = ContextImpl.createSystemUiContext(getSystemContext());
- }
- return mSystemUiContext;
- }
+ return getSystemUiContext(DEFAULT_DISPLAY);
}
/**
- * Create the context instance base on system resources & display information which used for UI.
+ * Gets the context instance base on system resources & display information which used for UI.
* @param displayId The ID of the display where the UI is shown.
* @see ContextImpl#createSystemUiContext(ContextImpl, int)
*/
- public ContextImpl createSystemUiContext(int displayId) {
- return ContextImpl.createSystemUiContext(getSystemUiContext(), displayId);
+ @NonNull
+ public ContextImpl getSystemUiContext(int displayId) {
+ synchronized (this) {
+ if (mDisplaySystemUiContexts == null) {
+ mDisplaySystemUiContexts = new SparseArray<>();
+ }
+ ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId);
+ if (systemUiContext == null) {
+ systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId);
+ mDisplaySystemUiContexts.put(displayId, systemUiContext);
+ }
+ return systemUiContext;
+ }
+ }
+
+ @Nullable
+ @Override
+ public ContextImpl getSystemUiContextNoCreate() {
+ synchronized (this) {
+ if (mDisplaySystemUiContexts == null) return null;
+ return mDisplaySystemUiContexts.get(DEFAULT_DISPLAY);
+ }
+ }
+
+ void onSystemUiContextCleanup(ContextImpl context) {
+ synchronized (this) {
+ if (mDisplaySystemUiContexts == null) return;
+ final int index = mDisplaySystemUiContexts.indexOfValue(context);
+ if (index >= 0) {
+ mDisplaySystemUiContexts.removeAt(index);
+ }
+ }
}
public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
@@ -3613,7 +3674,7 @@
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
- r.referrer, r.voiceInteractor, window, r.configCallback,
+ r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
if (customIntent != null) {
@@ -3767,7 +3828,7 @@
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
- if (id != Display.DEFAULT_DISPLAY) {
+ if (id != DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
@@ -4925,7 +4986,8 @@
Slog.w(TAG, "Activity top position already set to onTop=" + onTop);
return;
}
- throw new IllegalStateException("Activity top position already set to onTop=" + onTop);
+ // TODO(b/209744518): Remove this short-term workaround while fixing the binder failure.
+ Slog.e(TAG, "Activity top position already set to onTop=" + onTop);
}
r.isTopResumedActivity = onTop;
@@ -5455,8 +5517,8 @@
} else {
final ViewRootImpl viewRoot = v.getViewRootImpl();
if (viewRoot != null) {
- // Clear the callback to avoid the destroyed activity from receiving
- // configuration changes that are no longer effective.
+ // Clear callbacks to avoid the destroyed activity from receiving
+ // configuration or camera compat changes that are no longer effective.
viewRoot.setActivityConfigCallback(null);
}
wm.removeViewImmediate(v);
diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java
index bc698f6..b9ad5c3 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -28,7 +28,7 @@
interface ActivityThreadInternal {
ContextImpl getSystemContext();
- ContextImpl getSystemUiContext();
+ ContextImpl getSystemUiContextNoCreate();
boolean isInDensityCompatMode();
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 8637e31..58f60a6 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -154,9 +154,12 @@
int configDiff;
boolean equivalent;
+ // Get theme outside of synchronization to avoid nested lock.
+ final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
+ final ContextImpl systemUiContext = mActivityThread.getSystemUiContextNoCreate();
+ final Resources.Theme systemUiTheme =
+ systemUiContext != null ? systemUiContext.getTheme() : null;
synchronized (mResourcesManager) {
- final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
- final Resources.Theme systemUiTheme = mActivityThread.getSystemUiContext().getTheme();
if (mPendingConfiguration != null) {
if (!mPendingConfiguration.isOtherSeqNewer(config)) {
config = mPendingConfiguration;
@@ -207,7 +210,8 @@
systemTheme.rebase();
}
- if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
+ if (systemUiTheme != null
+ && (systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
systemUiTheme.rebase();
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b5ed171..fc1884a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1862,6 +1862,14 @@
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
+ // If we started a foreground service in the same package, remember the stack trace.
+ if (cn != null && requireForeground) {
+ if (cn.getPackageName().equals(getOpPackageName())) {
+ Service.setStartForegroundServiceStackTrace(cn.getClassName(),
+ new StackTrace("Last startServiceCommon() call for this service was "
+ + "made here"));
+ }
+ }
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2618,7 +2626,10 @@
overrideConfig, display.getDisplayAdjustments().getCompatibilityInfo(),
mResources.getLoaders()));
context.mDisplay = display;
- context.mContextType = CONTEXT_TYPE_DISPLAY_CONTEXT;
+ // Inherit context type if the container is from System or System UI context to bypass
+ // UI context check.
+ context.mContextType = mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
+ ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI : CONTEXT_TYPE_DISPLAY_CONTEXT;
// Display contexts and any context derived from a display context should always override
// the display that would otherwise be inherited from mToken (or the global configuration if
// mToken is null).
@@ -2671,7 +2682,8 @@
// Step 2. Create the base context of the window context, it will also create a Resources
// associated with the WindowTokenClient and set the token to the base context.
- final ContextImpl windowContextBase = createWindowContextBase(windowTokenClient, display);
+ final ContextImpl windowContextBase = createWindowContextBase(windowTokenClient,
+ display.getDisplayId());
// Step 3. Create a WindowContext instance and set it as the outer context of the base
// context to make the service obtained by #getSystemService(String) able to query
@@ -2696,9 +2708,7 @@
if (display == null) {
throw new IllegalArgumentException("Display must not be null");
}
- final ContextImpl tokenContext = createWindowContextBase(token, display);
- tokenContext.setResources(createWindowContextResources(tokenContext));
- return tokenContext;
+ return createWindowContextBase(token, display.getDisplayId());
}
/**
@@ -2706,13 +2716,13 @@
* window.
*
* @param token The token to associate with {@link Resources}
- * @param display The {@link Display} to associate with.
+ * @param displayId The ID of {@link Display} to associate with.
*
* @see #createWindowContext(Display, int, Bundle)
* @see #createTokenContext(IBinder, Display)
*/
@UiContext
- ContextImpl createWindowContextBase(@NonNull IBinder token, @NonNull Display display) {
+ ContextImpl createWindowContextBase(@NonNull IBinder token, int displayId) {
ContextImpl baseContext = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
mAttributionSource.getAttributionTag(),
mAttributionSource.getNext(),
@@ -2726,8 +2736,8 @@
baseContext.setResources(windowContextResources);
// Associate the display with window context resources so that configuration update from
// the server side will also apply to the display's metrics.
- baseContext.mDisplay = ResourcesManager.getInstance()
- .getAdjustedDisplay(display.getDisplayId(), windowContextResources);
+ baseContext.mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(displayId,
+ windowContextResources);
return baseContext;
}
@@ -2963,6 +2973,18 @@
mContentCaptureOptions = options;
}
+ @Override
+ protected void finalize() throws Throwable {
+ // If mToken is a WindowTokenClient, the Context is usually associated with a
+ // WindowContainer. We should detach from WindowContainer when the Context is finalized
+ // if this Context is not a WindowContext. WindowContext finalization is handled in
+ // WindowContext class.
+ if (mToken instanceof WindowTokenClient && mContextType != CONTEXT_TYPE_WINDOW_CONTEXT) {
+ ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
+ }
+ super.finalize();
+ }
+
@UnsupportedAppUsage
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
@@ -2983,22 +3005,13 @@
* @param displayId The ID of the display where the UI is shown.
*/
static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
- final LoadedApk packageInfo = systemContext.mPackageInfo;
- ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, null, null, 0, null, null);
- context.setResources(createResources(null, packageInfo, null, displayId, null,
- packageInfo.getCompatibilityInfo(), null));
- context.updateDisplay(displayId);
+ final WindowTokenClient token = new WindowTokenClient();
+ final ContextImpl context = systemContext.createWindowContextBase(token, displayId);
+ token.attachContext(context);
+ token.attachToDisplayContent(displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
- return context;
- }
- /**
- * The overloaded method of {@link #createSystemUiContext(ContextImpl, int)}.
- * Uses {@Code Display.DEFAULT_DISPLAY} as the target display.
- */
- static ContextImpl createSystemUiContext(ContextImpl systemContext) {
- return createSystemUiContext(systemContext, Display.DEFAULT_DISPLAY);
+ return context;
}
@UnsupportedAppUsage
@@ -3178,6 +3191,10 @@
final void performFinalCleanup(String who, String what) {
//Log.i(TAG, "Cleanup up context: " + this);
mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+ if (mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
+ && mToken instanceof WindowTokenClient) {
+ mMainThread.onSystemUiContextCleanup(this);
+ }
}
@UnsupportedAppUsage
@@ -3206,7 +3223,13 @@
@Override
public IBinder getWindowContextToken() {
- return mContextType == CONTEXT_TYPE_WINDOW_CONTEXT ? mToken : null;
+ switch (mContextType) {
+ case CONTEXT_TYPE_WINDOW_CONTEXT:
+ case CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI:
+ return mToken;
+ default:
+ return null;
+ }
}
private void checkMode(int mode) {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 9833ed6..3060353 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -151,6 +151,9 @@
private final Runnable mDismissAction = this::dismissDialog;
+ /** A {@link Runnable} to run instead of dismissing when {@link #dismiss()} is called. */
+ private Runnable mDismissOverride;
+
/**
* Creates a dialog window that uses the default dialog theme.
* <p>
@@ -370,6 +373,11 @@
*/
@Override
public void dismiss() {
+ if (mDismissOverride != null) {
+ mDismissOverride.run();
+ return;
+ }
+
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
@@ -1354,6 +1362,21 @@
mDismissMessage = msg;
}
+ /**
+ * Set a {@link Runnable} to run when this dialog is dismissed instead of directly dismissing
+ * it. This allows to animate the dialog in its window before dismissing it.
+ *
+ * Note that {@code override} should always end up calling this method with {@code null}
+ * followed by a call to {@link #dismiss() dismiss} to actually dismiss the dialog.
+ *
+ * @see #dismiss()
+ *
+ * @hide
+ */
+ public void setDismissOverride(@Nullable Runnable override) {
+ mDismissOverride = override;
+ }
+
/** @hide */
public boolean takeCancelAndDismissListeners(@Nullable String msg,
@Nullable OnCancelListener cancel, @Nullable OnDismissListener dismiss) {
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index aba6eb9..83c57c5 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -17,6 +17,7 @@
package android.app;
import android.app.ActivityManager;
+import android.app.ICompatCameraControlCallback;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@@ -143,4 +144,15 @@
/** Reports that the splash screen view has attached to activity. */
oneway void splashScreenAttached(in IBinder token);
+
+ /**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
+ *
+ * @param token The token for the window that needs a control.
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ oneway void requestCompatCameraControl(in IBinder token, boolean showControl,
+ boolean transformationApplied, in ICompatCameraControlCallback callback);
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 9e9e28b..64a9c44 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -321,6 +321,8 @@
boolean isTopActivityImmersive();
void crashApplicationWithType(int uid, int initialPid, in String packageName, int userId,
in String message, boolean force, int exceptionTypeId);
+ void crashApplicationWithTypeWithExtras(int uid, int initialPid, in String packageName,
+ int userId, in String message, boolean force, int exceptionTypeId, in Bundle extras);
/** @deprecated -- use getProviderMimeTypeAsync */
@UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives =
"Use {@link android.content.ContentResolver#getType} public API instead.")
@@ -338,6 +340,8 @@
void setPackageScreenCompatMode(in String packageName, int mode);
@UnsupportedAppUsage
boolean switchUser(int userid);
+ String getSwitchingFromUserMessage();
+ String getSwitchingToUserMessage();
@UnsupportedAppUsage
void setStopUserOnSwitch(int value);
boolean removeTask(int taskId);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index d6ff6d3..448c3cf 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -107,7 +107,7 @@
void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
void scheduleSuicide();
void dispatchPackageBroadcast(int cmd, in String[] packages);
- void scheduleCrash(in String msg, int typeId);
+ void scheduleCrash(in String msg, int typeId, in Bundle extras);
void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path,
in ParcelFileDescriptor fd, in RemoteCallback finishCallback);
void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerInternal.java b/core/java/android/app/ICompatCameraControlCallback.aidl
similarity index 63%
rename from core/java/android/hardware/devicestate/DeviceStateManagerInternal.java
rename to core/java/android/app/ICompatCameraControlCallback.aidl
index 4c91c16..1a7f210 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerInternal.java
+++ b/core/java/android/app/ICompatCameraControlCallback.aidl
@@ -14,15 +14,17 @@
* limitations under the License.
*/
-package android.hardware.devicestate;
+package android.app;
/**
- * Device state manager local system service interface.
+ * This callback allows ActivityRecord to ask the calling View to apply the treatment for stretched
+ * issues affecting camera viewfinders when the user clicks on the camera compat control.
*
- * @hide Only for use within the system server.
+ * {@hide}
*/
-public abstract class DeviceStateManagerInternal {
+oneway interface ICompatCameraControlCallback {
- /** Returns the list of currently supported device state identifiers. */
- public abstract int[] getSupportedStateIdentifiers();
+ void applyCameraCompatTreatment();
+
+ void revertCameraCompatTreatment();
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index fd6fa57..9f4107c 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1259,7 +1259,7 @@
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration(), null /* referrer */, null /* voiceInteractor */,
- null /* window */, null /* activityConfigCallback */, null /*assistToken*/,
+ null /* window */, null /* activityCallback */, null /*assistToken*/,
null /*shareableActivityToken*/);
return activity;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9605cbd..2c02be7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5810,6 +5810,7 @@
p, result);
buildCustomContentIntoTemplate(mContext, standard, customContent,
p, result);
+ makeHeaderExpanded(standard);
return standard;
}
diff --git a/core/java/android/app/RemoteServiceException.java b/core/java/android/app/RemoteServiceException.java
index 1038530..e220627 100644
--- a/core/java/android/app/RemoteServiceException.java
+++ b/core/java/android/app/RemoteServiceException.java
@@ -16,6 +16,10 @@
package android.app;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
import android.util.AndroidRuntimeException;
/**
@@ -33,6 +37,10 @@
super(msg);
}
+ public RemoteServiceException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
/**
* Exception used to crash an app process when it didn't call {@link Service#startForeground}
* in time after the service was started with
@@ -44,8 +52,21 @@
/** The type ID passed to {@link IApplicationThread#scheduleCrash}. */
public static final int TYPE_ID = 1;
- public ForegroundServiceDidNotStartInTimeException(String msg) {
- super(msg);
+ private static final String KEY_SERVICE_CLASS_NAME = "serviceclassname";
+
+ public ForegroundServiceDidNotStartInTimeException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ public static Bundle createExtrasForService(@NonNull ComponentName service) {
+ Bundle b = new Bundle();
+ b.putString(KEY_SERVICE_CLASS_NAME, service.getClassName());
+ return b;
+ }
+
+ @Nullable
+ public static String getServiceClassNameFromExtras(@Nullable Bundle extras) {
+ return (extras == null) ? null : extras.getString(KEY_SERVICE_CLASS_NAME);
}
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 3363872..5a5ccb5 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -33,9 +33,12 @@
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.contentcapture.ContentCaptureManager;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -733,6 +736,7 @@
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, id,
notification, 0, FOREGROUND_SERVICE_TYPE_MANIFEST);
+ clearStartForegroundServiceStackTrace();
} catch (RemoteException ex) {
}
}
@@ -786,6 +790,7 @@
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, id,
notification, 0, foregroundServiceType);
+ clearStartForegroundServiceStackTrace();
} catch (RemoteException ex) {
}
}
@@ -941,4 +946,34 @@
private IActivityManager mActivityManager = null;
@UnsupportedAppUsage
private boolean mStartCompatibility = false;
+
+ /**
+ * This keeps track of the stacktrace where Context.startForegroundService() was called
+ * for each service class. We use that when we crash the app for not calling
+ * {@link #startForeground} in time, in {@link ActivityThread#throwRemoteServiceException}.
+ */
+ @GuardedBy("sStartForegroundServiceStackTraces")
+ private static final ArrayMap<String, StackTrace> sStartForegroundServiceStackTraces =
+ new ArrayMap<>();
+
+ /** @hide */
+ public static void setStartForegroundServiceStackTrace(
+ @NonNull String className, @NonNull StackTrace stacktrace) {
+ synchronized (sStartForegroundServiceStackTraces) {
+ sStartForegroundServiceStackTraces.put(className, stacktrace);
+ }
+ }
+
+ private void clearStartForegroundServiceStackTrace() {
+ synchronized (sStartForegroundServiceStackTraces) {
+ sStartForegroundServiceStackTraces.remove(this.getClassName());
+ }
+ }
+
+ /** @hide */
+ public static StackTrace getStartForegroundServiceStackTrace(@NonNull String className) {
+ synchronized (sStartForegroundServiceStackTraces) {
+ return sStartForegroundServiceStackTraces.get(className);
+ }
+ }
}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/app/StackTrace.java
similarity index 75%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to core/java/android/app/StackTrace.java
index 3729c09..ec058f8 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/java/android/app/StackTrace.java
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-package android.window;
+package android.app;
/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
+ * An Exception subclass that's used only for logging stacktraces.
* @hide
*/
-parcelable TaskFragmentAppearedInfo;
+public class StackTrace extends Exception {
+ public StackTrace(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index ddde272..cd885c1 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -19,6 +19,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -39,6 +40,8 @@
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
@@ -256,6 +259,51 @@
*/
public boolean isSleeping;
+ /**
+ * Camera compat control isn't shown because it's not requested by heuristics.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_HIDDEN = 0;
+
+ /**
+ * Camera compat control is shown with the treatment suggested.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED = 1;
+
+ /**
+ * Camera compat control is shown to allow reverting the applied treatment.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED = 2;
+
+ /**
+ * Camera compat control is dismissed by user.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_DISMISSED = 3;
+
+ /**
+ * Enum for the Camera app compat control states.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CAMERA_COMPAT_CONTROL_" }, value = {
+ CAMERA_COMPAT_CONTROL_HIDDEN,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED,
+ CAMERA_COMPAT_CONTROL_DISMISSED,
+ })
+ public @interface CameraCompatControlState {};
+
+ /**
+ * State of the Camera app compat control which is used to correct stretched viewfinder
+ * in apps that don't handle all possible configurations and changes between them correctly.
+ * @hide
+ */
+ @CameraCompatControlState
+ public int cameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
+
TaskInfo() {
// Do nothing
}
@@ -324,6 +372,17 @@
launchCookies.add(cookie);
}
+ /** @hide */
+ public boolean hasCameraCompatControl() {
+ return cameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
+ && cameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
+ }
+
+ /** @hide */
+ public boolean hasCompatUI() {
+ return hasCameraCompatControl() || topActivityInSizeCompat;
+ }
+
/**
* @return {@code true} if this task contains the launch cookie.
* @hide
@@ -376,19 +435,20 @@
* @return {@code true} if parameters that are important for size compat have changed.
* @hide
*/
- public boolean equalsForSizeCompat(@Nullable TaskInfo that) {
+ public boolean equalsForCompatUi(@Nullable TaskInfo that) {
if (that == null) {
return false;
}
return displayId == that.displayId
&& taskId == that.taskId
&& topActivityInSizeCompat == that.topActivityInSizeCompat
- // Bounds are important if top activity is in size compat
- && (!topActivityInSizeCompat || configuration.windowConfiguration.getBounds()
+ && cameraCompatControlState == that.cameraCompatControlState
+ // Bounds are important if top activity has compat controls.
+ && (!hasCompatUI() || configuration.windowConfiguration.getBounds()
.equals(that.configuration.windowConfiguration.getBounds()))
- && (!topActivityInSizeCompat || configuration.getLayoutDirection()
+ && (!hasCompatUI() || configuration.getLayoutDirection()
== that.configuration.getLayoutDirection())
- && (!topActivityInSizeCompat || isVisible == that.isVisible);
+ && (!hasCompatUI() || isVisible == that.isVisible);
}
/**
@@ -428,6 +488,7 @@
topActivityInSizeCompat = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
displayAreaFeatureId = source.readInt();
+ cameraCompatControlState = source.readInt();
}
/**
@@ -468,6 +529,7 @@
dest.writeBoolean(topActivityInSizeCompat);
dest.writeTypedObject(mTopActivityLocusId, flags);
dest.writeInt(displayAreaFeatureId);
+ dest.writeInt(cameraCompatControlState);
}
@Override
@@ -498,6 +560,22 @@
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ + " cameraCompatControlState="
+ + cameraCompatControlStateToString(cameraCompatControlState)
+ "}";
}
+
+ /** @hide */
+ public static String cameraCompatControlStateToString(
+ @CameraCompatControlState int cameraCompatControlState) {
+ switch (cameraCompatControlState) {
+ case CAMERA_COMPAT_CONTROL_HIDDEN: return "hidden";
+ case CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED: return "treatment-suggested";
+ case CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED: return "treatment-applied";
+ case CAMERA_COMPAT_CONTROL_DISMISSED: return "dismissed";
+ default:
+ throw new AssertionError(
+ "Unexpected camera compat control state: " + cameraCompatControlState);
+ }
+ }
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 22b1c03..3712caed 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -365,17 +365,18 @@
private int mCachedWallpaperUserId;
private Bitmap mDefaultWallpaper;
private Handler mMainLooperHandler;
- private ArrayMap<RectF, ArraySet<LocalWallpaperColorConsumer>> mLocalColorAreas =
- new ArrayMap<>();
+ private ArrayMap<LocalWallpaperColorConsumer, ArraySet<RectF>> mLocalColorCallbackAreas =
+ new ArrayMap<>();
private ILocalWallpaperColorConsumer mLocalColorCallback =
new ILocalWallpaperColorConsumer.Stub() {
@Override
public void onColorsChanged(RectF area, WallpaperColors colors) {
- ArraySet<LocalWallpaperColorConsumer> callbacks =
- mLocalColorAreas.get(area);
- if (callbacks == null) return;
- for (LocalWallpaperColorConsumer callback: callbacks) {
- callback.onColorsChanged(area, colors);
+ for (LocalWallpaperColorConsumer callback :
+ mLocalColorCallbackAreas.keySet()) {
+ ArraySet<RectF> areas = mLocalColorCallbackAreas.get(callback);
+ if (areas != null && areas.contains(area)) {
+ callback.onColorsChanged(area, colors);
+ }
}
}
};
@@ -420,46 +421,52 @@
}
}
- public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback,
+ public void addOnColorsChangedListener(
+ @NonNull LocalWallpaperColorConsumer callback,
@NonNull List<RectF> regions, int which, int userId, int displayId) {
- for (RectF area: regions) {
- ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area);
- if (callbacks == null) {
- callbacks = new ArraySet<>();
- mLocalColorAreas.put(area, callbacks);
+ synchronized (this) {
+ for (RectF area : regions) {
+ ArraySet<RectF> areas = mLocalColorCallbackAreas.get(callback);
+ if (areas == null) {
+ areas = new ArraySet<>();
+ mLocalColorCallbackAreas.put(callback, areas);
+ }
+ areas.add(area);
}
- callbacks.add(callback);
- }
- try {
- mService.addOnLocalColorsChangedListener(mLocalColorCallback , regions, which,
- userId, displayId);
- } catch (RemoteException e) {
- // Can't get colors, connection lost.
- Log.e(TAG, "Can't register for local color updates", e);
+ try {
+ // one way returns immediately
+ mService.addOnLocalColorsChangedListener(mLocalColorCallback, regions, which,
+ userId, displayId);
+ } catch (RemoteException e) {
+ // Can't get colors, connection lost.
+ Log.e(TAG, "Can't register for local color updates", e);
+ }
}
}
public void removeOnColorsChangedListener(
@NonNull LocalWallpaperColorConsumer callback, int which, int userId,
int displayId) {
- final ArrayList<RectF> removeAreas = new ArrayList<>();
- for (RectF area : mLocalColorAreas.keySet()) {
- ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area);
- if (callbacks == null) continue;
- callbacks.remove(callback);
- if (callbacks.size() == 0) {
- mLocalColorAreas.remove(area);
- removeAreas.add(area);
+ synchronized (this) {
+ final ArraySet<RectF> removeAreas = mLocalColorCallbackAreas.remove(callback);
+ if (removeAreas == null || removeAreas.size() == 0) {
+ return;
}
- }
- try {
- if (removeAreas.size() > 0) {
- mService.removeOnLocalColorsChangedListener(
- mLocalColorCallback, removeAreas, which, userId, displayId);
+ for (LocalWallpaperColorConsumer cb : mLocalColorCallbackAreas.keySet()) {
+ ArraySet<RectF> areas = mLocalColorCallbackAreas.get(cb);
+ if (areas != null && cb != callback) removeAreas.removeAll(areas);
}
- } catch (RemoteException e) {
- // Can't get colors, connection lost.
- Log.e(TAG, "Can't unregister for local color updates", e);
+ try {
+ if (removeAreas.size() > 0) {
+ // one way returns immediately
+ mService.removeOnLocalColorsChangedListener(
+ mLocalColorCallback, new ArrayList(removeAreas), which, userId,
+ displayId);
+ }
+ } catch (RemoteException e) {
+ // Can't get colors, connection lost.
+ Log.e(TAG, "Can't unregister for local color updates", e);
+ }
}
}
@@ -1481,18 +1488,27 @@
mContext.getUserId());
if (fd != null) {
FileOutputStream fos = null;
- boolean ok = false;
+ final Bitmap tmp = BitmapFactory.decodeStream(resources.openRawResource(resid));
try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- copyStreamToWallpaperFile(resources.openRawResource(resid), fos);
- // The 'close()' is the trigger for any server-side image manipulation,
- // so we must do that before waiting for completion.
- fos.close();
- completion.waitForCompletion();
+ // If the stream can't be decoded, treat it as an invalid input.
+ if (tmp != null) {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ tmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
+ // The 'close()' is the trigger for any server-side image manipulation,
+ // so we must do that before waiting for completion.
+ fos.close();
+ completion.waitForCompletion();
+ } else {
+ throw new IllegalArgumentException(
+ "Resource 0x" + Integer.toHexString(resid) + " is invalid");
+ }
} finally {
// Might be redundant but completion shouldn't wait unless the write
// succeeded; this is a fallback if it threw past the close+wait.
IoUtils.closeQuietly(fos);
+ if (tmp != null) {
+ tmp.recycle();
+ }
}
}
} catch (RemoteException e) {
@@ -1734,13 +1750,22 @@
result, which, completion, mContext.getUserId());
if (fd != null) {
FileOutputStream fos = null;
+ final Bitmap tmp = BitmapFactory.decodeStream(bitmapData);
try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- copyStreamToWallpaperFile(bitmapData, fos);
- fos.close();
- completion.waitForCompletion();
+ // If the stream can't be decoded, treat it as an invalid input.
+ if (tmp != null) {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ tmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
+ fos.close();
+ completion.waitForCompletion();
+ } else {
+ throw new IllegalArgumentException("InputStream is invalid");
+ }
} finally {
IoUtils.closeQuietly(fos);
+ if (tmp != null) {
+ tmp.recycle();
+ }
}
}
} catch (RemoteException e) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 556dfc7..3553748 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -679,8 +679,9 @@
* A String extra holding the time zone {@link android.app.AlarmManager} that the device
* will be set to.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an NFC bump.
+ * <p>Use only for device owner provisioning. This extra can be returned by the admin app when
+ * performing the admin-integrated provisioning flow as a result of the {@link
+ * #ACTION_GET_PROVISIONING_MODE} activity.
*/
public static final String EXTRA_PROVISIONING_TIME_ZONE
= "android.app.extra.PROVISIONING_TIME_ZONE";
@@ -689,8 +690,9 @@
* A Long extra holding the wall clock time (in milliseconds) to be set on the device's
* {@link android.app.AlarmManager}.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an NFC bump.
+ * <p>Use only for device owner provisioning. This extra can be returned by the admin app when
+ * performing the admin-integrated provisioning flow as a result of the {@link
+ * #ACTION_GET_PROVISIONING_MODE} activity.
*/
public static final String EXTRA_PROVISIONING_LOCAL_TIME
= "android.app.extra.PROVISIONING_LOCAL_TIME";
@@ -699,8 +701,9 @@
* A String extra holding the {@link java.util.Locale} that the device will be set to.
* Format: xx_yy, where xx is the language code, and yy the country code.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an NFC bump.
+ * <p>Use only for device owner provisioning. This extra can be returned by the admin app when
+ * performing the admin-integrated provisioning flow as a result of the {@link
+ * #ACTION_GET_PROVISIONING_MODE} activity.
*/
public static final String EXTRA_PROVISIONING_LOCALE
= "android.app.extra.PROVISIONING_LOCALE";
@@ -996,7 +999,10 @@
* The default for this extra is {@code false} - by default, the admin of a fully-managed
* device has the ability to grant sensors-related permissions.
*
- * <p>Use only for device owner provisioning.
+ * <p>Use only for device owner provisioning. This extra can be returned by the
+ * admin app when performing the admin-integrated provisioning flow as a result of the
+ * {@link #ACTION_GET_PROVISIONING_MODE} activity.
+ *
* @see #ACTION_GET_PROVISIONING_MODE
*/
public static final String EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT =
@@ -1065,6 +1071,9 @@
*
* <p>From {@link android.os.Build.VERSION_CODES#N} onwards, this is also supported for an
* intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE}.
+ *
+ * <p>This extra can also be returned by the admin app when performing the admin-integrated
+ * provisioning flow as a result of the {@link #ACTION_GET_PROVISIONING_MODE} activity.
*/
public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION =
"android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
@@ -1104,8 +1113,9 @@
*
* <p> Maximum 3 key-value pairs can be specified. The rest will be ignored.
*
- * <p> Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE} or
- * {@link #ACTION_PROVISION_MANAGED_DEVICE}
+ * <p> Can be used in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE}. This
+ * extra can also be returned by the admin app when performing the admin-integrated
+ * provisioning flow as a result of the {@link #ACTION_GET_PROVISIONING_MODE} activity.
*/
public static final String EXTRA_PROVISIONING_DISCLAIMERS =
"android.app.extra.PROVISIONING_DISCLAIMERS";
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index b5298fc..d1ef591 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1010,12 +1010,12 @@
* This change id restricts treatments that force a given min aspect ratio to activities
* whose orientation is fixed to portrait.
*
- * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * This treatment is enabled by default and only takes effect if OVERRIDE_MIN_ASPECT_RATIO is
+ * also enabled.
* @hide
*/
@ChangeId
@Overridable
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S_V2)
@TestApi
public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // buganizer id
@@ -1367,18 +1367,18 @@
* Returns if the activity should never be sandboxed to the activity window bounds.
* @hide
*/
- public boolean neverSandboxDisplayApis() {
+ public boolean neverSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS)
- || ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo);
+ || constrainDisplayApisConfig.getNeverConstrainDisplayApis(applicationInfo);
}
/**
* Returns if the activity should always be sandboxed to the activity window bounds.
* @hide
*/
- public boolean alwaysSandboxDisplayApis() {
+ public boolean alwaysSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
- || ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo);
+ || constrainDisplayApisConfig.getAlwaysConstrainDisplayApis(applicationInfo);
}
/** @hide */
diff --git a/core/java/android/content/pm/ConstrainDisplayApisConfig.java b/core/java/android/content/pm/ConstrainDisplayApisConfig.java
index 11ba3d4..98b73aa 100644
--- a/core/java/android/content/pm/ConstrainDisplayApisConfig.java
+++ b/core/java/android/content/pm/ConstrainDisplayApisConfig.java
@@ -19,10 +19,15 @@
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.Pair;
import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
+
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* Class for processing flags in the Device Config namespace 'constrain_display_apis'.
@@ -55,19 +60,45 @@
"always_constrain_display_apis";
/**
+ * Indicates that display APIs should never be constrained to the activity window bounds for all
+ * packages.
+ */
+ private boolean mNeverConstrainDisplayApisAllPackages;
+
+ /**
+ * Indicates that display APIs should never be constrained to the activity window bounds for
+ * a set of defined packages. Map keys are package names, and entries are a
+ * 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private ArrayMap<String, Pair<Long, Long>> mNeverConstrainConfigMap;
+
+ /**
+ * Indicates that display APIs should always be constrained to the activity window bounds for
+ * a set of defined packages. Map keys are package names, and entries are a
+ * 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private ArrayMap<String, Pair<Long, Long>> mAlwaysConstrainConfigMap;
+
+ public ConstrainDisplayApisConfig() {
+ updateCache();
+
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ BackgroundThread.getExecutor(), properties -> updateCache());
+ }
+
+ /**
* Returns true if either the flag 'never_constrain_display_apis_all_packages' is true or the
* flag 'never_constrain_display_apis' contains a package entry that matches the given {@code
* applicationInfo}.
*
* @param applicationInfo Information about the application/package.
*/
- public static boolean neverConstrainDisplayApis(ApplicationInfo applicationInfo) {
- if (DeviceConfig.getBoolean(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
- FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false)) {
+ public boolean getNeverConstrainDisplayApis(ApplicationInfo applicationInfo) {
+ if (mNeverConstrainDisplayApisAllPackages) {
return true;
}
- return flagHasMatchingPackageEntry(FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, applicationInfo);
+ return flagHasMatchingPackageEntry(mNeverConstrainConfigMap, applicationInfo);
}
/**
@@ -76,73 +107,106 @@
*
* @param applicationInfo Information about the application/package.
*/
- public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
- return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, applicationInfo);
+ public boolean getAlwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
+ return flagHasMatchingPackageEntry(mAlwaysConstrainConfigMap, applicationInfo);
+ }
+
+
+ /**
+ * Updates {@link #mNeverConstrainDisplayApisAllPackages}, {@link #mNeverConstrainConfigMap},
+ * and {@link #mAlwaysConstrainConfigMap} from the {@link DeviceConfig}.
+ */
+ private void updateCache() {
+ mNeverConstrainDisplayApisAllPackages = DeviceConfig.getBoolean(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false);
+
+ final String neverConstrainConfigStr = DeviceConfig.getString(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ "");
+ mNeverConstrainConfigMap = buildConfigMap(neverConstrainConfigStr);
+
+ final String alwaysConstrainConfigStr = DeviceConfig.getString(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ "");
+ mAlwaysConstrainConfigMap = buildConfigMap(alwaysConstrainConfigStr);
+ }
+
+ /**
+ * Processes the configuration string into a map of version codes, for the given
+ * configuration to be applied to the specified packages. If the given package
+ * entry string is invalid, then the map will not contain an entry for the package.
+ *
+ * @param configStr A configuration string expected to be in the format of a list of package
+ * entries separated by ','. A package entry expected to be in the format
+ * '<package-name>:<min-version-code>?:<max-version-code>?'.
+ * @return a map of configuration entries, where each key is a package name. Each value is
+ * a pair of version codes, in the format 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private static ArrayMap<String, Pair<Long, Long>> buildConfigMap(String configStr) {
+ ArrayMap<String, Pair<Long, Long>> configMap = new ArrayMap<>();
+ // String#split returns a non-empty array given an empty string.
+ if (configStr.isEmpty()) {
+ return configMap;
+ }
+ for (String packageEntryString : configStr.split(",")) {
+ List<String> packageAndVersions = Arrays.asList(packageEntryString.split(":", 3));
+ if (packageAndVersions.size() != 3) {
+ Slog.w(TAG, "Invalid package entry in flag 'never/always_constrain_display_apis': "
+ + packageEntryString);
+ // Skip this entry.
+ continue;
+ }
+ String packageName = packageAndVersions.get(0);
+ String minVersionCodeStr = packageAndVersions.get(1);
+ String maxVersionCodeStr = packageAndVersions.get(2);
+ try {
+ final long minVersion =
+ minVersionCodeStr.isEmpty() ? Long.MIN_VALUE : Long.parseLong(
+ minVersionCodeStr);
+ final long maxVersion =
+ maxVersionCodeStr.isEmpty() ? Long.MAX_VALUE : Long.parseLong(
+ maxVersionCodeStr);
+ Pair<Long, Long> minMaxVersionCodes = new Pair<>(minVersion, maxVersion);
+ configMap.put(packageName, minMaxVersionCodes);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryString);
+ // Skip this entry.
+ }
+ }
+ return configMap;
}
/**
* Returns true if the flag with the given {@code flagName} contains a package entry that
* matches the given {@code applicationInfo}.
*
+ * @param configMap the map representing the current configuration value to examine
* @param applicationInfo Information about the application/package.
*/
- private static boolean flagHasMatchingPackageEntry(String flagName,
+ private static boolean flagHasMatchingPackageEntry(Map<String, Pair<Long, Long>> configMap,
ApplicationInfo applicationInfo) {
- String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
- flagName, /* defaultValue= */ "");
-
- // String#split returns a non-empty array given an empty string.
- if (configStr.isEmpty()) {
+ if (configMap.isEmpty()) {
return false;
}
-
- for (String packageEntryString : configStr.split(",")) {
- if (matchesApplicationInfo(packageEntryString, applicationInfo)) {
- return true;
- }
+ if (!configMap.containsKey(applicationInfo.packageName)) {
+ return false;
}
-
- return false;
+ return matchesApplicationInfo(configMap.get(applicationInfo.packageName), applicationInfo);
}
/**
- * Parses the given {@code packageEntryString} and returns true if {@code
- * applicationInfo.packageName} matches the package name in the config and {@code
- * applicationInfo.longVersionCode} is within the version range in the config.
+ * Parses the given {@code minMaxVersionCodes} and returns true if {@code
+ * applicationInfo.longVersionCode} is within the version range in the pair.
+ * Returns false otherwise.
*
- * <p>Logs a warning and returns false in case the given {@code packageEntryString} is invalid.
- *
- * @param packageEntryStr A package entry expected to be in the format
- * '<package-name>:<min-version-code>?:<max-version-code>?'.
+ * @param minMaxVersionCodes A pair expected to be in the format
+ * 'Pair(<min-version-code>, <max-version-code>)'.
* @param applicationInfo Information about the application/package.
*/
- private static boolean matchesApplicationInfo(String packageEntryStr,
+ private static boolean matchesApplicationInfo(Pair<Long, Long> minMaxVersionCodes,
ApplicationInfo applicationInfo) {
- List<String> packageAndVersions = Arrays.asList(packageEntryStr.split(":", 3));
- if (packageAndVersions.size() != 3) {
- Slog.w(TAG, "Invalid package entry in flag 'never_constrain_display_apis': "
- + packageEntryStr);
- return false;
- }
- String packageName = packageAndVersions.get(0);
- String minVersionCodeStr = packageAndVersions.get(1);
- String maxVersionCodeStr = packageAndVersions.get(2);
-
- if (!packageName.equals(applicationInfo.packageName)) {
- return false;
- }
- long version = applicationInfo.longVersionCode;
- try {
- if (!minVersionCodeStr.isEmpty() && version < Long.parseLong(minVersionCodeStr)) {
- return false;
- }
- if (!maxVersionCodeStr.isEmpty() && version > Long.parseLong(maxVersionCodeStr)) {
- return false;
- }
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryStr);
- return false;
- }
- return true;
+ return applicationInfo.longVersionCode >= minMaxVersionCodes.first
+ && applicationInfo.longVersionCode <= minMaxVersionCodes.second;
}
}
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 43ef33e..28046c5 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -151,6 +151,12 @@
int BIOMETRIC_ERROR_RE_ENROLL = 16;
/**
+ * The privacy setting has been enabled and will block use of the sensor.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED = 18;
+
+ /**
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
* because the authentication attempt was unsuccessful.
* @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index fe43c83..fd46f24 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -69,7 +69,7 @@
BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
BIOMETRIC_ERROR_RE_ENROLL,
- FACE_ERROR_UNKNOWN
+ FACE_ERROR_UNKNOWN,
})
@Retention(RetentionPolicy.SOURCE)
@interface FaceError {}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 9f77a7e..0b02a91 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2632,7 +2632,8 @@
* </tbody>
* </table>
* <p>For applications targeting SDK version 31 or newer, if the mobile device declares to be
- * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS media performance class} S,
+ * media performance class 12 or higher by setting
+ * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger,
* the primary camera devices (first rear/front camera in the camera ID list) will not
* support JPEG sizes smaller than 1080p. If the application configures a JPEG stream
* smaller than 1080p, the camera device will round up the JPEG image size to at least
@@ -2705,9 +2706,11 @@
* </tbody>
* </table>
* <p>For applications targeting SDK version 31 or newer, if the mobile device doesn't declare
- * to be media performance class S, or if the camera device isn't a primary rear/front
- * camera, the minimum required output stream configurations are the same as for applications
- * targeting SDK version older than 31.</p>
+ * to be media performance class 12 or better by setting
+ * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger,
+ * or if the camera device isn't a primary rear/front camera, the minimum required output
+ * stream configurations are the same as for applications targeting SDK version older than
+ * 31.</p>
* <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional
* mandatory stream configurations on a per-capability basis.</p>
* <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 8864939..9b19fc4 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -110,6 +110,11 @@
private int mRepeatingRequestId = REQUEST_ID_NONE;
// Latest repeating request list's types
private int[] mRepeatingRequestTypes;
+
+ // Cache failed requests to process later in case of a repeating error callback
+ private int mFailedRepeatingRequestId = REQUEST_ID_NONE;
+ private int[] mFailedRepeatingRequestTypes;
+
// Map stream IDs to input/output configurations
private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
new SimpleEntry<>(REQUEST_ID_NONE, null);
@@ -1326,16 +1331,25 @@
int requestId = mRepeatingRequestId;
mRepeatingRequestId = REQUEST_ID_NONE;
+ mFailedRepeatingRequestId = REQUEST_ID_NONE;
int[] requestTypes = mRepeatingRequestTypes;
mRepeatingRequestTypes = null;
+ mFailedRepeatingRequestTypes = null;
long lastFrameNumber;
try {
lastFrameNumber = mRemoteDevice.cancelRequest(requestId);
} catch (IllegalArgumentException e) {
if (DEBUG) {
- Log.v(TAG, "Repeating request was already stopped for request " + requestId);
+ Log.v(TAG, "Repeating request was already stopped for request " +
+ requestId);
}
+ // Cache request id and request types in case of a race with
+ // "onRepeatingRequestError" which may no yet be scheduled on another thread
+ // or blocked by us.
+ mFailedRepeatingRequestId = requestId;
+ mFailedRepeatingRequestTypes = requestTypes;
+
// Repeating request was already stopped. Nothing more to do.
return;
}
@@ -1965,7 +1979,17 @@
synchronized(mInterfaceLock) {
// Camera is already closed or no repeating request is present.
if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) {
- return; // Camera already closed
+ if ((mFailedRepeatingRequestId == repeatingRequestId) &&
+ (mFailedRepeatingRequestTypes != null) && (mRemoteDevice != null)) {
+ Log.v(TAG, "Resuming stop of failed repeating request with id: " +
+ mFailedRepeatingRequestId);
+
+ checkEarlyTriggerSequenceCompleteLocked(mFailedRepeatingRequestId,
+ lastFrameNumber, mFailedRepeatingRequestTypes);
+ mFailedRepeatingRequestId = REQUEST_ID_NONE;
+ mFailedRepeatingRequestTypes = null;
+ }
+ return;
}
// Redirect device callback to the offline session in case we are in the middle
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 7d71984..0b1ed65 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -109,7 +109,8 @@
/** {@hide} */
public boolean quickPickupSensorEnabled(int user) {
- return !TextUtils.isEmpty(quickPickupSensorType())
+ return boolSettingDefaultOn(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, user)
+ && !TextUtils.isEmpty(quickPickupSensorType())
&& pickupGestureEnabled(user)
&& !alwaysOnEnabled(user);
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 5bb51c1..4f20553 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -35,7 +35,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
/**
* Display manager local system service interface.
@@ -130,14 +129,6 @@
public abstract DisplayInfo getDisplayInfo(int displayId);
/**
- * Returns a set of DisplayInfo, for the states that may be assumed by either the given display,
- * or any other display within that display's group.
- *
- * @param displayId The logical display id to fetch DisplayInfo for.
- */
- public abstract Set<DisplayInfo> getPossibleDisplayInfo(int displayId);
-
- /**
* Returns the position of the display's projection.
*
* @param displayId The logical display id.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 9721279..e0324c0 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -395,7 +395,7 @@
/**
* @hide
- * The IME is visible.
+ * The IME is perceptibly visible to the user.
*/
public static final int IME_VISIBLE = 0x2;
@@ -406,6 +406,15 @@
*/
public static final int IME_INVISIBLE = 0x4;
+ /**
+ * @hide
+ * The IME is visible, but not yet perceptible to the user (e.g. fading in)
+ * by {@link android.view.WindowInsetsController}.
+ *
+ * @see InputMethodManager#reportPerceptible
+ */
+ public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x8;
+
// Min and max values for back disposition.
private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index 74b814e..c8b4226e 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -45,6 +45,8 @@
// Last UID/GID of the range the AppZygote can setuid()/setgid() to
private final int mZygoteUidGidMax;
+ private final int mZygoteRuntimeFlags;
+
private final Object mLock = new Object();
/**
@@ -56,11 +58,13 @@
private final ApplicationInfo mAppInfo;
- public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax) {
+ public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax,
+ int runtimeFlags) {
mAppInfo = appInfo;
mZygoteUid = zygoteUid;
mZygoteUidGidMin = uidGidMin;
mZygoteUidGidMax = uidGidMax;
+ mZygoteRuntimeFlags = runtimeFlags;
}
/**
@@ -110,7 +114,7 @@
mZygoteUid,
mZygoteUid,
null, // gids
- 0, // runtimeFlags
+ mZygoteRuntimeFlags, // runtimeFlags
"app_zygote", // seInfo
abi, // abi
abi, // acceptedAbiList
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 136e3de..da47859 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -63,7 +63,7 @@
mMemoryRegistration = new MemoryRegistration(mSize);
mCleaner = Cleaner.create(mFileDescriptor,
- new Closer(mFileDescriptor, mMemoryRegistration));
+ new Closer(mFileDescriptor.getInt$(), mMemoryRegistration));
}
/**
@@ -256,6 +256,7 @@
*/
@Override
public void close() {
+ mFileDescriptor.setInt$(-1);
if (mCleaner != null) {
mCleaner.clean();
mCleaner = null;
@@ -305,10 +306,10 @@
* Cleaner that closes the FD
*/
private static final class Closer implements Runnable {
- private FileDescriptor mFd;
+ private int mFd;
private MemoryRegistration mMemoryReference;
- private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
+ private Closer(int fd, MemoryRegistration memoryReference) {
mFd = fd;
mMemoryReference = memoryReference;
}
@@ -316,7 +317,9 @@
@Override
public void run() {
try {
- Os.close(mFd);
+ FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(mFd);
+ Os.close(fd);
} catch (ErrnoException e) { /* swallow error */ }
mMemoryReference.release();
mMemoryReference = null;
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 55b1f940..07f4082 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -73,6 +73,15 @@
"[^/]*BatteryConsumer[^/]*\\.java"
],
"name": "BatteryUsageStatsProtoTests"
+ },
+ {
+ "file_patterns": ["SharedMemory[^/]*\\.java"],
+ "name": "CtsOsTestCases",
+ "options": [
+ {
+ "include-filter": "android.os.cts.SharedMemoryTest"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 20f6c10..cf2361a 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -199,22 +199,24 @@
// if any link in the chain is finished, remove the chain. Then, find any other chains that
// contain this op/package/uid/tag combination, and remove them, as well.
// TODO ntmyren: be smarter about this
- mAttributionChains.remove(attributionChainId);
- int numChains = mAttributionChains.size();
- ArrayList<Integer> toRemove = new ArrayList<>();
- for (int i = 0; i < numChains; i++) {
- int chainId = mAttributionChains.keyAt(i);
- ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i);
- int chainSize = chain.size();
- for (int j = 0; j < chainSize; j++) {
- AccessChainLink link = chain.get(j);
- if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) {
- toRemove.add(chainId);
- break;
+ synchronized(mAttributionChains) {
+ mAttributionChains.remove(attributionChainId);
+ int numChains = mAttributionChains.size();
+ ArrayList<Integer> toRemove = new ArrayList<>();
+ for (int i = 0; i < numChains; i++) {
+ int chainId = mAttributionChains.keyAt(i);
+ ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i);
+ int chainSize = chain.size();
+ for (int j = 0; j < chainSize; j++) {
+ AccessChainLink link = chain.get(j);
+ if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) {
+ toRemove.add(chainId);
+ break;
+ }
}
}
+ mAttributionChains.removeAll(toRemove);
}
- mAttributionChains.removeAll(toRemove);
}
@Override
@@ -234,8 +236,10 @@
// If this is not a successful start, or it is not a chain, or it is untrusted, return
return;
}
- addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid,
- attributionTag, attributionFlags, attributionChainId);
+ synchronized(mAttributionChains) {
+ addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid,
+ attributionTag, attributionFlags, attributionChainId);
+ }
}
private void addLinkToChainIfNotPresent(String op, String packageName, int uid,
@@ -310,7 +314,7 @@
String permGroup = usedPermGroups.get(permGroupNum);
ArrayMap<OpUsage, CharSequence> usagesWithLabels =
- getUniqueUsagesWithLabels(rawUsages.get(permGroup));
+ getUniqueUsagesWithLabels(permGroup, rawUsages.get(permGroup));
if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
isPhone = true;
@@ -431,7 +435,8 @@
return ListFormatter.getInstance().format(labels);
}
- private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) {
+ private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(String permGroup,
+ List<OpUsage> usages) {
ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>();
if (usages == null || usages.isEmpty()) {
@@ -466,7 +471,7 @@
// If this usage has a proxy, but is not a proxy, it is the end of a chain.
// TODO remove once camera converted
if (!proxies.containsKey(usageAttr) && usage.proxy != null
- && !usage.op.equals(OPSTR_RECORD_AUDIO)) {
+ && !MICROPHONE.equals(permGroup)) {
proxyLabels.put(usage, new ArrayList<>());
proxyPackages.add(usage.getPackageIdHash());
}
@@ -538,48 +543,51 @@
// TODO ntmyren: remove this proxy logic once camera is converted to AttributionSource
// For now: don't add mic proxy usages
- if (!start.op.equals(OPSTR_RECORD_AUDIO)) {
+ if (!MICROPHONE.equals(permGroup)) {
usagesAndLabels.put(start,
proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
}
}
- for (int i = 0; i < mAttributionChains.size(); i++) {
- List<AccessChainLink> usageList = mAttributionChains.valueAt(i);
- int lastVisible = usageList.size() - 1;
- // TODO ntmyren: remove this mic code once camera is converted to AttributionSource
- // if the list is empty or incomplete, do not show it.
- if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
- || !usageList.get(0).isStart()
- || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
- continue;
- }
-
- //TODO ntmyren: remove once camera etc. etc.
- for (AccessChainLink link: usageList) {
- proxyPackages.add(link.usage.getPackageIdHash());
- }
-
- AccessChainLink start = usageList.get(0);
- AccessChainLink lastVisibleLink = usageList.get(lastVisible);
- while (lastVisible > 0 && !shouldShowPackage(lastVisibleLink.usage.packageName)) {
- lastVisible--;
- lastVisibleLink = usageList.get(lastVisible);
- }
- String proxyLabel = null;
- if (!lastVisibleLink.usage.packageName.equals(start.usage.packageName)) {
- try {
- PackageManager userPkgManager =
- getUserContext(lastVisibleLink.usage.getUser()).getPackageManager();
- ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
- lastVisibleLink.usage.packageName, 0);
- proxyLabel = appInfo.loadLabel(userPkgManager).toString();
- } catch (PackageManager.NameNotFoundException e) {
- // do nothing
+ synchronized (mAttributionChains) {
+ for (int i = 0; i < mAttributionChains.size(); i++) {
+ List<AccessChainLink> usageList = mAttributionChains.valueAt(i);
+ int lastVisible = usageList.size() - 1;
+ // TODO ntmyren: remove this mic code once camera is converted to AttributionSource
+ // if the list is empty or incomplete, do not show it.
+ if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
+ || !usageList.get(0).isStart()
+ || !permGroup.equals(getGroupForOp(usageList.get(0).usage.op))
+ || !MICROPHONE.equals(permGroup)) {
+ continue;
}
+ //TODO ntmyren: remove once camera etc. etc.
+ for (AccessChainLink link : usageList) {
+ proxyPackages.add(link.usage.getPackageIdHash());
+ }
+
+ AccessChainLink start = usageList.get(0);
+ AccessChainLink lastVisibleLink = usageList.get(lastVisible);
+ while (lastVisible > 0 && !shouldShowPackage(lastVisibleLink.usage.packageName)) {
+ lastVisible--;
+ lastVisibleLink = usageList.get(lastVisible);
+ }
+ String proxyLabel = null;
+ if (!lastVisibleLink.usage.packageName.equals(start.usage.packageName)) {
+ try {
+ PackageManager userPkgManager =
+ getUserContext(lastVisibleLink.usage.getUser()).getPackageManager();
+ ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
+ lastVisibleLink.usage.packageName, 0);
+ proxyLabel = appInfo.loadLabel(userPkgManager).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ // do nothing
+ }
+
+ }
+ usagesAndLabels.put(start.usage, proxyLabel);
}
- usagesAndLabels.put(start.usage, proxyLabel);
}
for (int packageHash : mostRecentUsages.keySet()) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fdd3836..54952fc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -953,6 +953,22 @@
public static final String ACTION_LOCKSCREEN_SETTINGS = "android.settings.LOCK_SCREEN_SETTINGS";
/**
+ * Activity Action: Show settings to allow pairing bluetooth devices.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_BLUETOOTH_PAIRING_SETTINGS =
+ "android.settings.BLUETOOTH_PAIRING_SETTINGS";
+
+ /**
* Activity Action: Show settings to configure input methods, in particular
* allowing the user to enable input methods.
* <p>
@@ -6348,6 +6364,27 @@
public static final String ALLOW_MOCK_LOCATION = "mock_location";
/**
+ * This is used by Bluetooth Manager to store adapter name
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_NAME = "bluetooth_name";
+
+ /**
+ * This is used by Bluetooth Manager to store adapter address
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_ADDRESS = "bluetooth_address";
+
+ /**
+ * This is used by Bluetooth Manager to store whether adapter address is valid
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
+
+ /**
* Setting to indicate that on device captions are enabled.
*
* @hide
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 4d0fc16..ee32ce4 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1750,8 +1750,9 @@
/**
* Called when there has been a failure transferring the {@link AssistStructure} to
* the assistant. This may happen, for example, if the data is too large and results
- * in an out of memory exception, or the client has provided corrupt data. This will
- * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
+ * in an out of memory exception, the data has been cleared during transferring due to
+ * the new incoming assist data, or the client has provided corrupt data. This will be
+ * called immediately before {@link #onHandleAssist} and the AssistStructure supplied
* there afterwards will be null.
*
* @param failure The failure exception that was thrown when building the
@@ -1789,7 +1790,8 @@
* Called to receive data from the application that the user was currently viewing when
* an assist session is started. If the original show request did not specify
* {@link #SHOW_WITH_ASSIST}, {@link AssistState} parameter will only provide
- * {@link ActivityId}.
+ * {@link ActivityId}. If there was a failure to write the assist data to
+ * {@link AssistStructure}, the {@link AssistState#getAssistStructure()} will return null.
*
* <p>This method is called for all activities along with an index and count that indicates
* which activity the data is for. {@code index} will be between 0 and {@code count}-1 and
@@ -2214,7 +2216,8 @@
* @return If available, the structure definition of all windows currently
* displayed by the app. May be null if assist data has been disabled by the user
* or device policy; will be null if the original show request did not specify
- * {@link #SHOW_WITH_ASSIST}; will be an empty stub if the application has disabled assist
+ * {@link #SHOW_WITH_ASSIST} or the assist data has been corrupt when writing the data to
+ * {@link AssistStructure}; will be an empty stub if the application has disabled assist
* by marking its window as secure.
*/
public @Nullable AssistStructure getAssistStructure() {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 7b8410b..b68717a 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -196,7 +196,7 @@
EngineWindowPage[] mWindowPages = new EngineWindowPage[1];
Bitmap mLastScreenshot;
int mLastWindowPage = -1;
- float mLastPageOffset = 0;
+ private boolean mResetWindowPages;
// Copies from mIWallpaperEngine.
HandlerCaller mCaller;
@@ -787,7 +787,7 @@
Log.w(TAG, "Can't notify system because wallpaper connection "
+ "was not established.");
}
- resetWindowPages();
+ mResetWindowPages = true;
processLocalColors(mPendingXOffset, mPendingXOffsetStep);
} catch (RemoteException e) {
Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
@@ -1515,6 +1515,7 @@
float finalXOffsetStep = xOffsetStep;
float finalXOffset = xOffset;
mHandler.post(() -> {
+ resetWindowPages();
int xPage = xCurrentPage;
EngineWindowPage current;
if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) {
@@ -1699,15 +1700,15 @@
private void resetWindowPages() {
if (supportsLocalColorExtraction()) return;
+ if (!mResetWindowPages) return;
+ mResetWindowPages = false;
mLastWindowPage = -1;
- mHandler.post(() -> {
- for (int i = 0; i < mWindowPages.length; i++) {
- EngineWindowPage page = mWindowPages[i];
- if (page != null) {
- page.setLastUpdateTime(0L);
- }
+ for (int i = 0; i < mWindowPages.length; i++) {
+ EngineWindowPage page = mWindowPages[i];
+ if (page != null) {
+ page.setLastUpdateTime(0L);
}
- });
+ }
}
private int getRectFPage(RectF area, float step) {
@@ -1730,40 +1731,8 @@
if (DEBUG) {
Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
}
-
- List<WallpaperColors> colors = getLocalWallpaperColors(regions);
mHandler.post(() -> {
- float step = mPendingXOffsetStep;
- if (!validStep(step)) {
- step = 0;
- }
- for (int i = 0; i < regions.size(); i++) {
- RectF area = regions.get(i);
- if (!isValid(area)) continue;
- int pageInx = getRectFPage(area, step);
- // no page should be null
- EngineWindowPage page = mWindowPages[pageInx];
-
- if (page != null) {
- mLocalColorAreas.add(area);
- page.addArea(area);
- WallpaperColors color = colors.get(i);
- if (color != null && !color.equals(page.getColors(area))) {
- page.addWallpaperColors(area, color);
- }
- } else {
- mLocalColorsToAdd.add(area);
- }
- }
- for (int i = 0; i < colors.size() && colors.get(i) != null; i++) {
- try {
- mConnection.onLocalWallpaperColorsChanged(regions.get(i), colors.get(i),
- mDisplayContext.getDisplayId());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
- return;
- }
- }
+ mLocalColorsToAdd.addAll(regions);
processLocalColors(mPendingXOffset, mPendingYOffset);
});
@@ -1784,88 +1753,15 @@
if (!validStep(step)) {
return;
}
- for (int i = 0; i < regions.size(); i++) {
- RectF area = regions.get(i);
- if (!isValid(area)) continue;
- int pageInx = getRectFPage(area, step);
- // no page should be null
- EngineWindowPage page = mWindowPages[pageInx];
- if (page != null) {
- page.removeArea(area);
+ for (int i = 0; i < mWindowPages.length; i++) {
+ for (int j = 0; j < regions.size(); j++) {
+ EngineWindowPage page = mWindowPages[i];
+ if (page != null) page.removeArea(regions.get(j));
}
}
});
}
- private @NonNull List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas) {
- ArrayList<WallpaperColors> colors = new ArrayList<>(areas.size());
- float step = mPendingXOffsetStep;
- if (!validStep(step)) {
- if (DEBUG) Log.d(TAG, "invalid step size " + step);
- step = 1.0f;
- }
- for (int i = 0; i < areas.size(); i++) {
- RectF currentArea = areas.get(i);
- if (currentArea == null || !isValid(currentArea)) {
- Log.wtf(TAG, "invalid local area " + currentArea);
- continue;
- }
- EngineWindowPage page;
- RectF area;
- int pageIndx;
- synchronized (mLock) {
- pageIndx = getRectFPage(currentArea, step);
- if (mWindowPages.length == 0 || pageIndx < 0
- || pageIndx > mWindowPages.length || !isValid(currentArea)) {
- colors.add(null);
- continue;
- }
- area = generateSubRect(currentArea, pageIndx, mWindowPages.length);
- page = mWindowPages[pageIndx];
- }
- if (page == null) {
- colors.add(null);
- continue;
- }
- float finalStep = step;
- int finalPageIndx = pageIndx;
- Bitmap screenShot = page.getBitmap();
- if (screenShot == null) screenShot = mLastScreenshot;
- if (screenShot == null || screenShot.isRecycled()) {
- if (DEBUG) {
- Log.d(TAG, "invalid bitmap " + screenShot
- + " for page " + finalPageIndx);
- }
- page.setLastUpdateTime(0);
- colors.add(null);
- continue;
- }
- Bitmap b = screenShot;
- Rect subImage = new Rect(
- Math.round(area.left * b.getWidth() / finalStep),
- Math.round(area.top * b.getHeight()),
- Math.round(area.right * b.getWidth() / finalStep),
- Math.round(area.bottom * b.getHeight())
- );
- subImage = fixRect(b, subImage);
- if (DEBUG) {
- Log.d(TAG, "getting subbitmap of " + subImage.toString()
- + " for RectF " + area.toString()
- + " screenshot width " + screenShot.getWidth() + " height "
- + screenShot.getHeight());
- }
- Bitmap colorImg = Bitmap.createBitmap(screenShot,
- subImage.left, subImage.top, subImage.width(), subImage.height());
- if (DEBUG) {
- Log.d(TAG, "created bitmap " + colorImg.getWidth() + ", "
- + colorImg.getHeight());
- }
- WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
- colors.add(color);
- }
- return colors;
- }
-
// fix the rect to be included within the bounds of the bitmap
private Rect fixRect(Bitmap b, Rect r) {
r.left = r.left >= r.right || r.left >= b.getWidth() || r.left > 0
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index bdfa700..9cdd54c 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -17,8 +17,10 @@
package android.text.style;
import android.annotation.NonNull;
+import android.content.res.Configuration;
import android.graphics.Paint;
import android.graphics.Typeface;
+import android.graphics.fonts.FontStyle;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
@@ -45,6 +47,7 @@
public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mStyle;
+ private final int mFontWeightAdjustment;
/**
* Creates a {@link StyleSpan} from a style.
@@ -54,7 +57,24 @@
* in {@link Typeface}.
*/
public StyleSpan(int style) {
+ this(style, Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED);
+ }
+
+ /**
+ * Creates a {@link StyleSpan} from a style and font weight adjustment.
+ *
+ * @param style An integer constant describing the style for this span. Examples
+ * include bold, italic, and normal. Values are constants defined
+ * in {@link Typeface}.
+ * @param fontWeightAdjustment An integer describing the adjustment to be made to the font
+ * weight.
+ * @see Configuration#fontWeightAdjustment This is the adjustment in text font weight
+ * that is used to reflect the current user's preference for increasing font weight.
+ * @hide
+ */
+ public StyleSpan(@Typeface.Style int style, int fontWeightAdjustment) {
mStyle = style;
+ mFontWeightAdjustment = fontWeightAdjustment;
}
/**
@@ -64,6 +84,7 @@
*/
public StyleSpan(@NonNull Parcel src) {
mStyle = src.readInt();
+ mFontWeightAdjustment = src.readInt();
}
@Override
@@ -91,6 +112,7 @@
@Override
public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mStyle);
+ dest.writeInt(mFontWeightAdjustment);
}
/**
@@ -100,17 +122,25 @@
return mStyle;
}
+ /**
+ * Returns the font weight adjustment specified by this span.
+ * @hide
+ */
+ public int getFontWeightAdjustment() {
+ return mFontWeightAdjustment;
+ }
+
@Override
public void updateDrawState(TextPaint ds) {
- apply(ds, mStyle);
+ apply(ds, mStyle, mFontWeightAdjustment);
}
@Override
public void updateMeasureState(TextPaint paint) {
- apply(paint, mStyle);
+ apply(paint, mStyle, mFontWeightAdjustment);
}
- private static void apply(Paint paint, int style) {
+ private static void apply(Paint paint, int style, int fontWeightAdjustment) {
int oldStyle;
Typeface old = paint.getTypeface();
@@ -129,6 +159,18 @@
tf = Typeface.create(old, want);
}
+ // Base typeface may already be bolded by auto bold. Bold further.
+ if ((style & Typeface.BOLD) != 0) {
+ if (fontWeightAdjustment != 0
+ && fontWeightAdjustment != Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED) {
+ int newWeight = Math.min(
+ Math.max(tf.getWeight() + fontWeightAdjustment, FontStyle.FONT_WEIGHT_MIN),
+ FontStyle.FONT_WEIGHT_MAX);
+ boolean italic = (want & Typeface.ITALIC) != 0;
+ tf = Typeface.create(tf, newWeight, italic);
+ }
+ }
+
int fake = want & ~tf.getStyle();
if ((fake & Typeface.BOLD) != 0) {
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4edff27..0b50192 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -646,7 +646,7 @@
e.fillInStackTrace();
Log.w(TAG, "New hash " + hash
+ " is before end of array hash " + mHashes[index-1]
- + " at index " + index + " key " + key, e);
+ + " at index " + index + (DEBUG ? " key " + key : ""), e);
put(key, value);
return;
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 86cf28f..e08b913 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -54,6 +54,10 @@
/** @hide */
public static final String SETTINGS_SUPPORT_LARGE_SCREEN = "settings_support_large_screen";
+ /** @hide */
+ public static final String SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS =
+ "settings_enable_monitor_phantom_procs";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -76,6 +80,7 @@
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
}
private static final Set<String> PERSISTENT_FLAGS;
@@ -83,6 +88,7 @@
PERSISTENT_FLAGS = new HashSet<>();
PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL);
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
+ PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
}
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index e7ff978..9cb0d1f 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1463,10 +1463,10 @@
return false;
}
final Configuration config = mResources.getConfiguration();
- // TODO(b/179308296) Temporarily - never report max bounds to only Launcher if the feature
- // is disabled.
+ // TODO(b/179308296) Temporarily exclude Launcher from being given max bounds, by checking
+ // if the caller is the recents component.
return config != null && !config.windowConfiguration.getMaxBounds().isEmpty()
- && (mDisplayInfo.shouldConstrainMetricsForLauncher || !isRecentsComponent());
+ && !isRecentsComponent();
}
/**
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 6572510..8e5f905 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -306,13 +306,6 @@
public float brightnessDefault;
/**
- * @hide
- * True if Display#getRealSize and getRealMetrics should be constrained for Launcher, false
- * otherwise.
- */
- public boolean shouldConstrainMetricsForLauncher = false;
-
- /**
* The {@link RoundedCorners} if present, otherwise {@code null}.
*/
@Nullable
@@ -388,8 +381,7 @@
&& brightnessMinimum == other.brightnessMinimum
&& brightnessMaximum == other.brightnessMaximum
&& brightnessDefault == other.brightnessDefault
- && Objects.equals(roundedCorners, other.roundedCorners)
- && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher;
+ && Objects.equals(roundedCorners, other.roundedCorners);
}
@Override
@@ -440,7 +432,6 @@
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
roundedCorners = other.roundedCorners;
- shouldConstrainMetricsForLauncher = other.shouldConstrainMetricsForLauncher;
}
public void readFromParcel(Parcel source) {
@@ -497,7 +488,6 @@
for (int i = 0; i < numUserDisabledFormats; i++) {
userDisabledHdrTypes[i] = source.readInt();
}
- shouldConstrainMetricsForLauncher = source.readBoolean();
}
@Override
@@ -552,7 +542,6 @@
for (int i = 0; i < userDisabledHdrTypes.length; i++) {
dest.writeInt(userDisabledHdrTypes[i]);
}
- dest.writeBoolean(shouldConstrainMetricsForLauncher);
}
@Override
@@ -807,8 +796,6 @@
sb.append(brightnessMaximum);
sb.append(", brightnessDefault ");
sb.append(brightnessDefault);
- sb.append(", shouldConstrainMetricsForLauncher ");
- sb.append(shouldConstrainMetricsForLauncher);
sb.append("}");
return sb.toString();
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9e41e4d..b64d25a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -33,7 +33,6 @@
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
import android.view.DisplayCutout;
-import android.view.DisplayInfo;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.ICrossWindowBlurEnabledListener;
@@ -746,17 +745,6 @@
out InsetsState outInsetsState);
/**
- * Returns a list of {@link android.view.DisplayInfo} for the logical display. This is not
- * guaranteed to include all possible device states. The list items are unique.
- *
- * If invoked through a package other than a launcher app, returns an empty list.
- *
- * @param displayId the id of the logical display
- * @param packageName the name of the calling package
- */
- List<DisplayInfo> getPossibleDisplayInfo(int displayId, String packageName);
-
- /**
* Called to show global actions.
*/
void showGlobalActions();
@@ -866,6 +854,23 @@
void attachWindowContextToWindowToken(IBinder clientToken, IBinder token);
/**
+ * Attaches a {@code clientToken} to associate with DisplayContent.
+ * <p>
+ * Note that this API should be invoked after calling
+ * {@link android.window.WindowTokenClient#attachContext(Context)}
+ * </p>
+ *
+ * @param clientToken {@link android.window.WindowContext#getWindowContextToken()
+ * the WindowContext's token}
+ * @param displayId The display associated with the window context
+ *
+ * @return the DisplayContent's {@link android.app.res.Configuration} if the Context is
+ * attached to the DisplayContent successfully. {@code null}, otherwise.
+ * @throws android.view.WindowManager.InvalidDisplayException if the display ID is invalid
+ */
+ Configuration attachToDisplayContent(IBinder clientToken, int displayId);
+
+ /**
* Detaches {@link android.window.WindowContext} from the window manager node it's currently
* attached to. It is no-op if the WindowContext is not attached to a window manager node.
*
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 02b2c5d..d609fb8 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -124,7 +124,12 @@
public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
int[] hideTypes) {
super.setControl(control, showTypes, hideTypes);
- if (control == null && !isRequestedVisibleAwaitingControl()) {
+ // TODO(b/204524304): clean-up how to deal with the timing issues of hiding IME:
+ // 1) Already requested show IME, in the meantime of WM callback the control but got null
+ // control when relayout comes first
+ // 2) Make sure no regression on some implicit request IME visibility calls (e.g.
+ // toggleSoftInput)
+ if (control == null && !mIsRequestedVisibleAwaitingControl) {
hide();
removeSurface();
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index adda721..9bf71ec 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1266,6 +1266,11 @@
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
cancelAnimation(runner, false /* invokeCallback */);
if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
+ if (runner.getAnimationType() == ANIMATION_TYPE_RESIZE) {
+ // The resize animation doesn't show or hide the insets. We shouldn't change the
+ // requested visibility.
+ return;
+ }
if (shown) {
showDirectly(runner.getTypes(), true /* fromIme */);
} else {
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index e1352dd..edcfc95 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -131,6 +131,9 @@
@Override
public boolean applyChangeInsets(InsetsState outState) {
+ if (mCancelled) {
+ return false;
+ }
final float fraction = mAnimation.getInterpolatedFraction();
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
final InsetsSource fromSource = mFromState.peekSource(type);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 3917279..75b69cb 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -202,7 +202,7 @@
@Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
- boolean[] typeVisibilityMap = new boolean[Type.SIZE];
+ boolean[] typeVisibilityMap = new boolean[SIZE];
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 40942ea7..80ffd40 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1860,7 +1860,7 @@
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
- xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
+ xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_CLASS_POINTER,
DEFAULT_DISPLAY);
}
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 046232a..2dac81c 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -129,7 +129,11 @@
* The index of the element in the tree in prefix order. This should be used for z-layering
* to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
* happen.
+ * @deprecated WindowManager may set a z-order different from the prefix order, and has set the
+ * correct layer for the animation leash already, so this should not be used for
+ * layer any more.
*/
+ @Deprecated
@UnsupportedAppUsage
public final int prefixOrderIndex;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index e8725f0..4c7bfd2 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -942,9 +942,10 @@
// When the listener is updated, we will get at least a single position update call so we can
// guarantee any changes we post will be applied.
private void replacePositionUpdateListener(int surfaceWidth, int surfaceHeight,
- @Nullable Transaction geometryTransaction) {
+ Transaction geometryTransaction) {
if (mPositionListener != null) {
mRenderNode.removePositionUpdateListener(mPositionListener);
+ geometryTransaction = mPositionListener.getTransaction().merge(geometryTransaction);
}
mPositionListener = new SurfaceViewPositionUpdateListener(surfaceWidth, surfaceHeight,
geometryTransaction);
@@ -952,7 +953,8 @@
}
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
- boolean creating, boolean sizeChanged, boolean hintChanged) {
+ boolean creating, boolean sizeChanged, boolean hintChanged,
+ Transaction geometryTransaction) {
boolean realSizeChanged = false;
mSurfaceLock.lock();
@@ -990,10 +992,6 @@
mSurfaceAlpha = alpha;
}
- // While creating the surface, we will set it's initial
- // geometry. Outside of that though, we should generally
- // leave it to the RenderThread.
- Transaction geometryTransaction = new Transaction();
geometryTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
if ((sizeChanged || hintChanged) && !creating) {
setBufferSize(geometryTransaction);
@@ -1016,20 +1014,18 @@
mSurfaceHeight);
}
- boolean applyChangesOnRenderThread =
- sizeChanged && !creating && isHardwareAccelerated();
if (isHardwareAccelerated()) {
// This will consume the passed in transaction and the transaction will be
// applied on a render worker thread.
replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight,
- applyChangesOnRenderThread ? geometryTransaction : null);
+ geometryTransaction);
}
if (DEBUG_POSITION) {
Log.d(TAG, String.format(
- "%d updateSurfacePosition %s"
+ "%d performSurfaceTransaction %s "
+ "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
System.identityHashCode(this),
- applyChangesOnRenderThread ? "RenderWorker" : "UiThread",
+ isHardwareAccelerated() ? "RenderWorker" : "UI Thread",
mScreenRect.left, mScreenRect.top, mScreenRect.right,
mScreenRect.bottom, mSurfaceWidth, mSurfaceHeight));
}
@@ -1141,12 +1137,14 @@
final Rect surfaceInsets = viewRoot.mWindowAttributes.surfaceInsets;
mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
-
+ // Collect all geometry changes and apply these changes on the RenderThread worker
+ // via the RenderNode.PositionUpdateListener.
+ final Transaction geometryTransaction = new Transaction();
if (creating) {
updateOpaqueFlag();
final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
if (mUseBlastAdapter) {
- createBlastSurfaceControls(viewRoot, name);
+ createBlastSurfaceControls(viewRoot, name, geometryTransaction);
} else {
mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot, name);
}
@@ -1155,7 +1153,7 @@
}
final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
- translator, creating, sizeChanged, hintChanged);
+ translator, creating, sizeChanged, hintChanged, geometryTransaction);
final boolean redrawNeeded = sizeChanged || creating || hintChanged
|| (mVisible && !mDrawFinished);
@@ -1322,7 +1320,8 @@
// is still alive, the old buffers will continue to be presented until replaced by buffers from
// the new adapter. This means we do not need to track the old surface control and destroy it
// after the client has drawn to avoid any flickers.
- private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name) {
+ private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name,
+ Transaction geometryTransaction) {
if (mSurfaceControl == null) {
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName(name)
@@ -1363,8 +1362,9 @@
}
mTransformHint = viewRoot.getBufferTransformHint();
mBlastSurfaceControl.setTransformHint(mTransformHint);
- mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
- mSurfaceHeight, mFormat);
+ mBlastBufferQueue = new BLASTBufferQueue(name);
+ mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat,
+ geometryTransaction);
}
private void onDrawFinished() {
@@ -1545,6 +1545,10 @@
applyOrMergeTransaction(mRtTransaction, frameNumber);
}
}
+
+ public Transaction getTransaction() {
+ return mPositionChangedTransaction;
+ }
}
private SurfaceViewPositionUpdateListener mPositionListener = null;
@@ -1638,6 +1642,11 @@
@Override
public void setFixedSize(int width, int height) {
if (mRequestedWidth != width || mRequestedHeight != height) {
+ if (DEBUG_POSITION) {
+ Log.d(TAG, String.format("%d setFixedSize %dx%d -> %dx%d",
+ System.identityHashCode(this), mRequestedWidth, mRequestedHeight, width,
+ height));
+ }
mRequestedWidth = width;
mRequestedHeight = height;
requestLayout();
@@ -1647,6 +1656,10 @@
@Override
public void setSizeFromLayout() {
if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+ if (DEBUG_POSITION) {
+ Log.d(TAG, String.format("%d setSizeFromLayout was %dx%d",
+ System.identityHashCode(this), mRequestedWidth, mRequestedHeight));
+ }
mRequestedWidth = mRequestedHeight = -1;
requestLayout();
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cdfd7be..74b9731 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -89,6 +89,7 @@
import android.annotation.UiContext;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.ICompatCameraControlCallback;
import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
@@ -329,7 +330,7 @@
private static final ArrayList<ConfigChangedCallback> sConfigCallbacks = new ArrayList<>();
/**
- * Callback for notifying activities about override configuration changes.
+ * Callback for notifying activities.
*/
public interface ActivityConfigCallback {
@@ -339,11 +340,23 @@
* @param newDisplayId New display id, {@link Display#INVALID_DISPLAY} if not changed.
*/
void onConfigurationChanged(Configuration overrideConfig, int newDisplayId);
+
+ /**
+ * Notify the corresponding activity about the request to show or hide a camera compat
+ * control for stretched issues in the viewfinder.
+ *
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback);
}
/**
- * Callback used to notify corresponding activity about override configuration change and make
- * sure that all resources are set correctly before updating the ViewRootImpl's internal state.
+ * Callback used to notify corresponding activity about camera compat control changes, override
+ * configuration change and make sure that all resources are set correctly before updating the
+ * ViewRootImpl's internal state.
*/
private ActivityConfigCallback mActivityConfigCallback;
@@ -768,6 +781,12 @@
private boolean mWaitForBlastSyncComplete = false;
/**
+ * Keeps track of the last frame number that was attempted to draw. Should only be accessed on
+ * the RenderThread.
+ */
+ private long mRtLastAttemptedDrawFrameNum = 0;
+
+ /**
* Keeps track of whether a traverse was triggered while the UI thread was paused. This can
* occur when the client is waiting on another process to submit the transaction that
* contains the buffer. The UI thread needs to wait on the callback before it can submit
@@ -889,7 +908,10 @@
}
}
- /** Add activity config callback to be notified about override config changes. */
+ /**
+ * Add activity config callback to be notified about override config changes and camera
+ * compat control state updates.
+ */
public void setActivityConfigCallback(ActivityConfigCallback callback) {
mActivityConfigCallback = callback;
}
@@ -4051,40 +4073,19 @@
}
/**
- * The callback will run on the render thread.
+ * Only call this on the UI Thread.
*/
- private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
- boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
- final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
- mBLASTDrawConsumer = null;
- return frameNr -> {
- if (DEBUG_BLAST) {
- Log.d(mTag, "Received frameCompleteCallback frameNum=" + frameNr);
- }
-
- handler.postAtFrontOfQueue(() -> {
- if (mNextDrawUseBlastSync) {
- // We don't need to synchronize mRtBLASTSyncTransaction here since we're
- // guaranteed that this is called after onFrameDraw and mNextDrawUseBlastSync
- // is only true when the UI thread is paused. Therefore, no one should be
- // modifying this object until the next vsync.
- mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
- if (blastSyncConsumer != null) {
- blastSyncConsumer.accept(mSurfaceChangedTransaction);
- }
- }
-
- if (reportNextDraw) {
- // TODO: Use the frame number
- pendingDrawFinished();
- }
- if (commitCallbacks != null) {
- for (int i = 0; i < commitCallbacks.size(); i++) {
- commitCallbacks.get(i).run();
- }
- }
- });
- };
+ void clearBlastSync() {
+ mNextDrawUseBlastSync = false;
+ mWaitForBlastSyncComplete = false;
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
+ + " due to a previous skipped traversal.");
+ }
+ if (mRequestedTraverseWhilePaused) {
+ mRequestedTraverseWhilePaused = false;
+ scheduleTraversals();
+ }
}
/**
@@ -4094,30 +4095,90 @@
return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
}
- private boolean addFrameCompleteCallbackIfNeeded() {
+ private boolean addFrameCompleteCallbackIfNeeded(boolean reportNextDraw) {
if (!isHardwareEnabled()) {
return false;
}
+ if (!mNextDrawUseBlastSync && !reportNextDraw) {
+ return false;
+ }
+
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Creating frameCompleteCallback");
+ }
+
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
+ long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Received frameCompleteCallback "
+ + " lastAcquiredFrameNum=" + frameNr
+ + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum);
+ }
+
+ boolean frameWasNotDrawn = frameNr != mRtLastAttemptedDrawFrameNum;
+ // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
+ // draw attempt. The next transaction and transaction complete callback were only set
+ // for the current draw attempt.
+ if (frameWasNotDrawn) {
+ mBlastBufferQueue.setNextTransaction(null);
+ mBlastBufferQueue.setTransactionCompleteCallback(mRtLastAttemptedDrawFrameNum,
+ null);
+ }
+
+ mHandler.postAtFrontOfQueue(() -> {
+ if (mNextDrawUseBlastSync) {
+ // We don't need to synchronize mRtBLASTSyncTransaction here since we're
+ // guaranteed that this is called after onFrameDraw and mNextDrawUseBlastSync
+ // is only true when the UI thread is paused. Therefore, no one should be
+ // modifying this object until the next vsync.
+ mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
+ if (mBLASTDrawConsumer != null) {
+ mBLASTDrawConsumer.accept(mSurfaceChangedTransaction);
+ }
+ mBLASTDrawConsumer = null;
+ }
+
+ if (reportNextDraw) {
+ pendingDrawFinished();
+ }
+
+ if (frameWasNotDrawn) {
+ clearBlastSync();
+ }
+ });
+ });
+ return true;
+ }
+
+ private void addFrameCommitCallbackIfNeeded() {
+ if (!isHardwareEnabled()) {
+ return;
+ }
+
ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
.captureFrameCommitCallbacks();
- final boolean needFrameCompleteCallback =
- mNextDrawUseBlastSync || mReportNextDraw
- || (commitCallbacks != null && commitCallbacks.size() > 0);
- if (needFrameCompleteCallback) {
- if (DEBUG_BLAST) {
- Log.d(mTag, "Creating frameCompleteCallback"
- + " mNextDrawUseBlastSync=" + mNextDrawUseBlastSync
- + " mReportNextDraw=" + mReportNextDraw
- + " commitCallbacks size="
- + (commitCallbacks == null ? 0 : commitCallbacks.size()));
- }
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(
- createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw,
- commitCallbacks));
- return true;
+ final boolean needFrameCommitCallback =
+ (commitCallbacks != null && commitCallbacks.size() > 0);
+ if (!needFrameCommitCallback) {
+ return;
}
- return false;
+
+ if (DEBUG_DRAW) {
+ Log.d(mTag, "Creating frameCommitCallback"
+ + " commitCallbacks size=" + commitCallbacks.size());
+ }
+ mAttachInfo.mThreadedRenderer.setFrameCommitCallback(didProduceBuffer -> {
+ if (DEBUG_DRAW) {
+ Log.d(mTag, "Received frameCommitCallback didProduceBuffer=" + didProduceBuffer);
+ }
+
+ mHandler.postAtFrontOfQueue(() -> {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ });
+ });
}
private void addFrameCallbackIfNeeded() {
@@ -4147,6 +4208,8 @@
+ " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
}
+ mRtLastAttemptedDrawFrameNum = frame;
+
if (needsCallbackForBlur) {
mBlurRegionAggregator
.dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
@@ -4169,18 +4232,7 @@
if (DEBUG_BLAST) {
Log.d(mTag, "Received transactionCompleteCallback frameNum=" + frame);
}
- mHandler.postAtFrontOfQueue(() -> {
- mNextDrawUseBlastSync = false;
- mWaitForBlastSyncComplete = false;
- if (DEBUG_BLAST) {
- Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
- + " due to a previous skipped traversal.");
- }
- if (mRequestedTraverseWhilePaused) {
- mRequestedTraverseWhilePaused = false;
- scheduleTraversals();
- }
- });
+ mHandler.postAtFrontOfQueue(this::clearBlastSync);
});
}
};
@@ -4201,8 +4253,9 @@
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
- boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
addFrameCallbackIfNeeded();
+ addFrameCommitCallbackIfNeeded();
+ boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(mReportNextDraw);
try {
boolean canUseAsync = draw(fullRedrawNeeded);
@@ -8374,7 +8427,7 @@
if (mTranslator != null) {
mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
}
- if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) {
+ if (insetsState != null && insetsState.getSourceOrDefaultVisibility(ITYPE_IME)) {
ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsChanged",
getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
}
@@ -8399,7 +8452,7 @@
mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
mTranslator.translateSourceControlsInScreenToAppWindow(activeControls);
}
- if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) {
+ if (insetsState != null && insetsState.getSourceOrDefaultVisibility(ITYPE_IME)) {
ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsControlChanged",
getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
}
@@ -10563,6 +10616,20 @@
}
/**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state
+ * for the corresponding activity.
+ *
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ public void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ mActivityConfigCallback.requestCompatCameraControl(
+ showControl, transformationApplied, callback);
+ }
+
+ /**
* Redirect the next draw of this ViewRoot (from the UI thread perspective)
* to the passed in consumer. This can be used to create P2P synchronization
* between ViewRoot's however it comes with many caveats.
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index cde1cc7..0f1a9d9 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -903,16 +903,6 @@
result.append(mPrivacyIndicatorBounds != null ? "privacyIndicatorBounds="
+ mPrivacyIndicatorBounds : "");
result.append("\n ");
- result.append("compatInsetsTypes=" + mCompatInsetsTypes);
- result.append("\n ");
- result.append("compatIgnoreVisibility=" + mCompatIgnoreVisibility);
- result.append("\n ");
- result.append("systemWindowInsetsConsumed=" + mSystemWindowInsetsConsumed);
- result.append("\n ");
- result.append("stableInsetsConsumed=" + mStableInsetsConsumed);
- result.append("\n ");
- result.append("displayCutoutConsumed=" + mDisplayCutoutConsumed);
- result.append("\n ");
result.append(isRound() ? "round" : "");
result.append("}");
return result.toString();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8764ccc..27c5ac2 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -123,7 +123,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -715,20 +714,6 @@
}
/**
- * Returns a set of {@link WindowMetrics} for the given display. Each WindowMetrics instance
- * is the maximum WindowMetrics for a device state, including rotations. This is not guaranteed
- * to include all possible device states.
- *
- * This API can only be used by Launcher.
- *
- * @param displayId the id of the logical display
- * @hide
- */
- default @NonNull Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
- throw new UnsupportedOperationException();
- }
-
- /**
* Used to asynchronously request Keyboard Shortcuts from the focused window.
*
* @hide
@@ -4769,6 +4754,16 @@
return Integer.toString(inputFeature);
}
}
+
+ /**
+ * True if the window should consume all pointer events itself, regardless of whether they
+ * are inside of the window. If the window is modal, its touchable region will expand to the
+ * size of its task.
+ * @hide
+ */
+ public boolean isModal() {
+ return (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+ }
}
/**
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 1c915cb..a2d3e34 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,9 +16,12 @@
package android.view;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.window.WindowProviderService.isWindowProviderService;
@@ -43,9 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -303,86 +304,34 @@
private WindowInsets computeWindowInsets(Rect bounds) {
// Initialize params which used for obtaining all system insets.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
final Context context = (mParentWindow != null) ? mParentWindow.getContext() : mContext;
params.token = Context.getToken(context);
- return getWindowInsetsFromServerForCurrentDisplay(params, bounds);
+ params.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ params.setFitInsetsTypes(0);
+ params.setFitInsetsSides(0);
+
+ return getWindowInsetsFromServer(params, bounds);
}
- private WindowInsets getWindowInsetsFromServerForCurrentDisplay(
- WindowManager.LayoutParams attrs, Rect bounds) {
- final Configuration config = mContext.getResources().getConfiguration();
- return getWindowInsetsFromServerForDisplay(mContext.getDisplayId(), attrs, bounds,
- config.isScreenRound(), config.windowConfiguration.getWindowingMode());
- }
-
- /**
- * Retrieves WindowInsets for the given context and display, given the window bounds.
- *
- * @param displayId the ID of the logical display to calculate insets for
- * @param attrs the LayoutParams for the calling app
- * @param bounds the window bounds to calculate insets for
- * @param isScreenRound if the display identified by displayId is round
- * @param windowingMode the windowing mode of the window to calculate insets for
- * @return WindowInsets calculated for the given window bounds, on the given display
- */
- private static WindowInsets getWindowInsetsFromServerForDisplay(int displayId,
- WindowManager.LayoutParams attrs, Rect bounds, boolean isScreenRound,
- int windowingMode) {
+ private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) {
try {
final InsetsState insetsState = new InsetsState();
final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
- .getWindowInsets(attrs, displayId, insetsState);
+ .getWindowInsets(attrs, mContext.getDisplayId(), insetsState);
+ final Configuration config = mContext.getResources().getConfiguration();
+ final boolean isScreenRound = config.isScreenRound();
+ final int windowingMode = config.windowConfiguration.getWindowingMode();
return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
- SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode,
- null /* typeSideMap */);
+ SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode, null /* typeSideMap */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
- @NonNull
- public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
- List<DisplayInfo> possibleDisplayInfos;
- try {
- possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService()
- .getPossibleDisplayInfo(displayId, mContext.getPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- Set<WindowMetrics> maxMetrics = new HashSet<>();
- WindowInsets windowInsets;
- DisplayInfo currentDisplayInfo;
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- for (int i = 0; i < possibleDisplayInfos.size(); i++) {
- currentDisplayInfo = possibleDisplayInfos.get(i);
-
- // Calculate max bounds for this rotation and state.
- Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
- currentDisplayInfo.logicalHeight);
-
- // Calculate insets for the rotated max bounds.
- final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
- // Initialize insets based upon display rotation. Note any window-provided insets
- // will not be set.
- windowInsets = getWindowInsetsFromServerForDisplay(
- currentDisplayInfo.displayId, params,
- new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
- currentDisplayInfo.getNaturalHeight()), isScreenRound,
- WINDOWING_MODE_FULLSCREEN);
- // Set the hardware-provided insets.
- windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
- currentDisplayInfo.roundedCorners)
- .setDisplayCutout(currentDisplayInfo.displayCutout).build();
-
- maxMetrics.add(new WindowMetrics(maxBounds, windowInsets));
- }
- return maxMetrics;
- }
-
- @Override
public void holdLock(IBinder token, int durationMs) {
try {
WindowManagerGlobal.getWindowManagerService().holdLock(token, durationMs);
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index e634d60..94f6333 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -236,6 +236,9 @@
*/
int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
+ // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
+ // make app pair split only have single root then we can just attach the
+ // divider to the single root task in shell.
int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3;
int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100;
int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101;
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 9241c30..cd3c8c9 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -483,6 +483,8 @@
/**
* Returns the component name of the system service that is consuming the captured events for
* the current user.
+ *
+ * @throws RuntimeException if getting the component name is timed out.
*/
@Nullable
public ComponentName getServiceComponentName() {
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 9d1bf17..fb534c7 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -435,7 +435,10 @@
if (view.getViewTranslationResponse() != null
&& view.getViewTranslationResponse().equals(response)) {
if (callback instanceof TextViewTranslationCallback) {
- if (((TextViewTranslationCallback) callback).isShowingTranslation()) {
+ TextViewTranslationCallback textViewCallback =
+ (TextViewTranslationCallback) callback;
+ if (textViewCallback.isShowingTranslation()
+ || textViewCallback.isAnimationRunning()) {
if (DEBUG) {
Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId
+ ". Ignoring.");
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 721260e..1244d75 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3682,14 +3682,14 @@
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
- invalidateTopGlow();
+ invalidateEdgeEffects();
} else if (incrementalDeltaY < 0) {
mEdgeGlowBottom.onPullDistance((float) overscroll / getHeight(),
1.f - (float) x / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
- invalidateBottomGlow();
+ invalidateEdgeEffects();
}
}
}
@@ -3729,7 +3729,7 @@
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
- invalidateTopGlow();
+ invalidateEdgeEffects();
} else if (rawDeltaY < 0) {
mEdgeGlowBottom.onPullDistance(
(float) -overScrollDistance / getHeight(),
@@ -3737,7 +3737,7 @@
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
- invalidateBottomGlow();
+ invalidateEdgeEffects();
}
}
}
@@ -3783,17 +3783,21 @@
// First allow releasing existing overscroll effect:
float consumed = 0;
if (mEdgeGlowTop.getDistance() != 0) {
- consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(),
- (float) x / getWidth());
- if (consumed != 0f) {
- invalidateTopGlow();
+ if (canScrollUp()) {
+ mEdgeGlowTop.onRelease();
+ } else {
+ consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(),
+ (float) x / getWidth());
}
+ invalidateEdgeEffects();
} else if (mEdgeGlowBottom.getDistance() != 0) {
- consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(),
- 1f - (float) x / getWidth());
- if (consumed != 0f) {
- invalidateBottomGlow();
+ if (canScrollDown()) {
+ mEdgeGlowBottom.onRelease();
+ } else {
+ consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(),
+ 1f - (float) x / getWidth());
}
+ invalidateEdgeEffects();
}
int pixelsConsumed = Math.round(consumed * getHeight());
return deltaY - pixelsConsumed;
@@ -3803,30 +3807,16 @@
* @return <code>true</code> if either the top or bottom edge glow is currently active or
* <code>false</code> if it has no value to release.
*/
- private boolean isGlowActive() {
- return mEdgeGlowBottom.getDistance() != 0 || mEdgeGlowTop.getDistance() != 0;
+ private boolean doesTouchStopStretch() {
+ return (mEdgeGlowBottom.getDistance() != 0 && !canScrollDown())
+ || (mEdgeGlowTop.getDistance() != 0 && !canScrollUp());
}
- private void invalidateTopGlow() {
+ private void invalidateEdgeEffects() {
if (!shouldDisplayEdgeEffects()) {
return;
}
- final boolean clipToPadding = getClipToPadding();
- final int top = clipToPadding ? mPaddingTop : 0;
- final int left = clipToPadding ? mPaddingLeft : 0;
- final int right = clipToPadding ? getWidth() - mPaddingRight : getWidth();
- invalidate(left, top, right, top + mEdgeGlowTop.getMaxHeight());
- }
-
- private void invalidateBottomGlow() {
- if (!shouldDisplayEdgeEffects()) {
- return;
- }
- final boolean clipToPadding = getClipToPadding();
- final int bottom = clipToPadding ? getHeight() - mPaddingBottom : getHeight();
- final int left = clipToPadding ? mPaddingLeft : 0;
- final int right = clipToPadding ? getWidth() - mPaddingRight : getWidth();
- invalidate(left, bottom - mEdgeGlowBottom.getMaxHeight(), right, bottom);
+ invalidate();
}
@Override
@@ -4469,7 +4459,7 @@
final int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess) + translateY;
canvas.translate(translateX, edgeY);
if (mEdgeGlowTop.draw(canvas)) {
- invalidateTopGlow();
+ invalidateEdgeEffects();
}
canvas.restoreToCount(restoreCount);
}
@@ -4483,7 +4473,7 @@
canvas.translate(edgeX, edgeY);
canvas.rotate(180, width, 0);
if (mEdgeGlowBottom.draw(canvas)) {
- invalidateBottomGlow();
+ invalidateEdgeEffects();
}
canvas.restoreToCount(restoreCount);
}
@@ -4573,7 +4563,7 @@
mActivePointerId = ev.getPointerId(0);
int motionPosition = findMotionRow(y);
- if (isGlowActive()) {
+ if (doesTouchStopStretch()) {
// Pressed during edge effect, so this is considered the same as a fling catch.
touchMode = mTouchMode = TOUCH_MODE_FLING;
} else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {
@@ -6579,7 +6569,7 @@
*/
public void setBottomEdgeEffectColor(@ColorInt int color) {
mEdgeGlowBottom.setColor(color);
- invalidateBottomGlow();
+ invalidateEdgeEffects();
}
/**
@@ -6593,7 +6583,7 @@
*/
public void setTopEdgeEffectColor(@ColorInt int color) {
mEdgeGlowTop.setColor(color);
- invalidateTopGlow();
+ invalidateEdgeEffects();
}
/**
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3d4d9ec..1784dcf 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -301,6 +301,13 @@
public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
/**
+ * A ReadWriteHelper which has the same behavior as ReadWriteHelper.DEFAULT, but which is
+ * intentionally a different instance in order to trick Bundle reader so that it doesn't allow
+ * lazy initialization.
+ */
+ private static final Parcel.ReadWriteHelper ALTERNATIVE_DEFAULT = new Parcel.ReadWriteHelper();
+
+ /**
* Used to restrict the views which can be inflated
*
* @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
@@ -1856,7 +1863,18 @@
this.value = in.readTypedObject(Bitmap.CREATOR);
break;
case BUNDLE:
- this.value = in.readBundle();
+ // Because we use Parcel.allowSquashing() when writing, and that affects
+ // how the contents of Bundles are written, we need to ensure the bundle is
+ // unparceled immediately, not lazily. Setting a custom ReadWriteHelper
+ // just happens to have that effect on Bundle.readFromParcel().
+ // TODO(b/212731590): build this state tracking into Bundle
+ if (in.hasReadWriteHelper()) {
+ this.value = in.readBundle();
+ } else {
+ in.setReadWriteHelper(ALTERNATIVE_DEFAULT);
+ this.value = in.readBundle();
+ in.setReadWriteHelper(null);
+ }
break;
case INTENT:
this.value = in.readTypedObject(Intent.CREATOR);
@@ -3696,18 +3714,21 @@
}
private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
+ if (hierarchyRoot == null) {
+ mBitmapCache = src.mBitmapCache;
+ mApplicationInfoCache = src.mApplicationInfoCache;
+ } else {
+ mBitmapCache = hierarchyRoot.mBitmapCache;
+ mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
+ }
if (hierarchyRoot == null || src.mIsRoot) {
// If there's no provided root, or if src was itself a root, then this RemoteViews is
// the root of the new hierarchy.
mIsRoot = true;
- mBitmapCache = new BitmapCache();
- mApplicationInfoCache = new ApplicationInfoCache();
hierarchyRoot = this;
} else {
// Otherwise, we're a descendant in the hierarchy.
mIsRoot = false;
- mBitmapCache = hierarchyRoot.mBitmapCache;
- mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
}
mApplication = src.mApplication;
mLayoutId = src.mLayoutId;
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 4a78f3e..942be21 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -46,6 +46,7 @@
private TranslationTransformationMethod mTranslationTransformation;
private boolean mIsShowingTranslation = false;
+ private boolean mAnimationRunning = false;
private boolean mIsTextPaddingEnabled = false;
private CharSequence mPaddedText;
private int mAnimationDurationMillis = 250; // default value
@@ -92,6 +93,7 @@
(TextView) view,
() -> {
mIsShowingTranslation = true;
+ mAnimationRunning = false;
// TODO(b/178353965): well-handle setTransformationMethod.
((TextView) view).setTransformationMethod(transformation);
});
@@ -124,6 +126,7 @@
(TextView) view,
() -> {
mIsShowingTranslation = false;
+ mAnimationRunning = false;
((TextView) view).setTransformationMethod(transformation);
});
if (!TextUtils.isEmpty(mContentDescription)) {
@@ -162,6 +165,13 @@
return mIsShowingTranslation;
}
+ /**
+ * Returns whether the view is running animation to show or hide the translation.
+ */
+ public boolean isAnimationRunning() {
+ return mAnimationRunning;
+ }
+
@Override
public void enableContentPadding() {
mIsTextPaddingEnabled = true;
@@ -230,6 +240,7 @@
mAnimator.end();
// Note: mAnimator is now null; do not use again here.
}
+ mAnimationRunning = true;
int fadedOutColor = colorWithAlpha(view.getCurrentTextColor(), 0);
mAnimator = ValueAnimator.ofArgb(view.getCurrentTextColor(), fadedOutColor);
mAnimator.addUpdateListener(
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 5eb432e..cdfa206 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -19,12 +19,11 @@
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
/** @hide */
oneway interface ITaskFragmentOrganizer {
- void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 1ad0452..4399207 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -44,4 +44,10 @@
* Unregisters remote animations per transition type for the organizer.
*/
void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
+
+ /**
+ * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
+ * only occupies a portion of Task bounds.
+ */
+ boolean isActivityEmbedded(in IBinder activityToken);
}
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index a833600..022d05d 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -66,4 +66,7 @@
* Restarts the top activity in the given task by killing its process if it is visible.
*/
void restartTaskTopActivityProcessIfVisible(in WindowContainerToken task);
+
+ /** Updates a state of camera compat control for stretched issues in the viewfinder. */
+ void updateCameraCompatControlState(in WindowContainerToken task, int state);
}
diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java
index dbf7eb3..2bf2f319 100644
--- a/core/java/android/window/PictureInPictureSurfaceTransaction.java
+++ b/core/java/android/window/PictureInPictureSurfaceTransaction.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,9 +35,10 @@
* @hide
*/
public final class PictureInPictureSurfaceTransaction implements Parcelable {
+ private static final float NOT_SET = -1f;
- public final float mPositionX;
- public final float mPositionY;
+ public final float mAlpha;
+ public final PointF mPosition;
public final float[] mFloat9;
@@ -45,33 +47,37 @@
public final float mCornerRadius;
- private final Rect mWindowCrop = new Rect();
+ private final Rect mWindowCrop;
- public PictureInPictureSurfaceTransaction(Parcel in) {
- mPositionX = in.readFloat();
- mPositionY = in.readFloat();
+ private PictureInPictureSurfaceTransaction(Parcel in) {
+ mAlpha = in.readFloat();
+ mPosition = in.readTypedObject(PointF.CREATOR);
mFloat9 = new float[9];
in.readFloatArray(mFloat9);
mRotation = in.readFloat();
mCornerRadius = in.readFloat();
- mWindowCrop.set(Objects.requireNonNull(in.readTypedObject(Rect.CREATOR)));
+ mWindowCrop = in.readTypedObject(Rect.CREATOR);
}
- public PictureInPictureSurfaceTransaction(float positionX, float positionY,
- float[] float9, float rotation, float cornerRadius,
+ private PictureInPictureSurfaceTransaction(float alpha, @Nullable PointF position,
+ @Nullable float[] float9, float rotation, float cornerRadius,
@Nullable Rect windowCrop) {
- mPositionX = positionX;
- mPositionY = positionY;
- mFloat9 = Arrays.copyOf(float9, 9);
- mRotation = rotation;
- mCornerRadius = cornerRadius;
- if (windowCrop != null) {
- mWindowCrop.set(windowCrop);
+ mAlpha = alpha;
+ mPosition = position;
+ if (float9 == null) {
+ mFloat9 = new float[9];
+ Matrix.IDENTITY_MATRIX.getValues(mFloat9);
+ mRotation = 0;
+ } else {
+ mFloat9 = Arrays.copyOf(float9, 9);
+ mRotation = rotation;
}
+ mCornerRadius = cornerRadius;
+ mWindowCrop = (windowCrop == null) ? null : new Rect(windowCrop);
}
public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) {
- this(other.mPositionX, other.mPositionY,
+ this(other.mAlpha, other.mPosition,
other.mFloat9, other.mRotation, other.mCornerRadius, other.mWindowCrop);
}
@@ -82,13 +88,18 @@
return matrix;
}
+ /** @return {@code true} if this transaction contains setting corner radius. */
+ public boolean hasCornerRadiusSet() {
+ return mCornerRadius > 0;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PictureInPictureSurfaceTransaction)) return false;
PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o;
- return Objects.equals(mPositionX, that.mPositionX)
- && Objects.equals(mPositionY, that.mPositionY)
+ return Objects.equals(mAlpha, that.mAlpha)
+ && Objects.equals(mPosition, that.mPosition)
&& Arrays.equals(mFloat9, that.mFloat9)
&& Objects.equals(mRotation, that.mRotation)
&& Objects.equals(mCornerRadius, that.mCornerRadius)
@@ -97,7 +108,7 @@
@Override
public int hashCode() {
- return Objects.hash(mPositionX, mPositionY, Arrays.hashCode(mFloat9),
+ return Objects.hash(mAlpha, mPosition, Arrays.hashCode(mFloat9),
mRotation, mCornerRadius, mWindowCrop);
}
@@ -108,8 +119,8 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeFloat(mPositionX);
- out.writeFloat(mPositionY);
+ out.writeFloat(mAlpha);
+ out.writeTypedObject(mPosition, 0 /* flags */);
out.writeFloatArray(mFloat9);
out.writeFloat(mRotation);
out.writeFloat(mCornerRadius);
@@ -120,8 +131,8 @@
public String toString() {
final Matrix matrix = getMatrix();
return "PictureInPictureSurfaceTransaction("
- + " posX=" + mPositionX
- + " posY=" + mPositionY
+ + " alpha=" + mAlpha
+ + " position=" + mPosition
+ " matrix=" + matrix.toShortString()
+ " rotation=" + mRotation
+ " cornerRadius=" + mCornerRadius
@@ -134,11 +145,20 @@
@NonNull SurfaceControl surfaceControl,
@NonNull SurfaceControl.Transaction tx) {
final Matrix matrix = surfaceTransaction.getMatrix();
- tx.setMatrix(surfaceControl, matrix, new float[9])
- .setPosition(surfaceControl,
- surfaceTransaction.mPositionX, surfaceTransaction.mPositionY)
- .setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop)
- .setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
+ tx.setMatrix(surfaceControl, matrix, new float[9]);
+ if (surfaceTransaction.mPosition != null) {
+ tx.setPosition(surfaceControl,
+ surfaceTransaction.mPosition.x, surfaceTransaction.mPosition.y);
+ }
+ if (surfaceTransaction.mWindowCrop != null) {
+ tx.setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop);
+ }
+ if (surfaceTransaction.hasCornerRadiusSet()) {
+ tx.setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
+ }
+ if (surfaceTransaction.mAlpha != NOT_SET) {
+ tx.setAlpha(surfaceControl, surfaceTransaction.mAlpha);
+ }
}
public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction>
@@ -151,4 +171,44 @@
return new PictureInPictureSurfaceTransaction[size];
}
};
+
+ public static class Builder {
+ private float mAlpha = NOT_SET;
+ private PointF mPosition;
+ private float[] mFloat9;
+ private float mRotation;
+ private float mCornerRadius = NOT_SET;
+ private Rect mWindowCrop;
+
+ public Builder setAlpha(float alpha) {
+ mAlpha = alpha;
+ return this;
+ }
+
+ public Builder setPosition(float x, float y) {
+ mPosition = new PointF(x, y);
+ return this;
+ }
+
+ public Builder setTransform(@NonNull float[] float9, float rotation) {
+ mFloat9 = Arrays.copyOf(float9, 9);
+ mRotation = rotation;
+ return this;
+ }
+
+ public Builder setCornerRadius(float cornerRadius) {
+ mCornerRadius = cornerRadius;
+ return this;
+ }
+
+ public Builder setWindowCrop(@NonNull Rect windowCrop) {
+ mWindowCrop = new Rect(windowCrop);
+ return this;
+ }
+
+ public PictureInPictureSurfaceTransaction build() {
+ return new PictureInPictureSurfaceTransaction(mAlpha, mPosition,
+ mFloat9, mRotation, mCornerRadius, mWindowCrop);
+ }
+ }
}
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index b9bf009..090dbff 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -43,6 +43,11 @@
*/
public interface SplashScreen {
/**
+ * The splash screen style is not defined.
+ * @hide
+ */
+ int SPLASH_SCREEN_STYLE_UNDEFINED = -1;
+ /**
* Force splash screen to be empty.
* @hide
*/
@@ -55,6 +60,7 @@
/** @hide */
@IntDef(prefix = { "SPLASH_SCREEN_STYLE_" }, value = {
+ SPLASH_SCREEN_STYLE_UNDEFINED,
SPLASH_SCREEN_STYLE_EMPTY,
SPLASH_SCREEN_STYLE_ICON
})
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java
deleted file mode 100644
index 89d9a95..0000000
--- a/core/java/android/window/TaskFragmentAppearedInfo.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.window;
-
-import android.annotation.NonNull;
-import android.annotation.TestApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.SurfaceControl;
-
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-@TestApi
-public final class TaskFragmentAppearedInfo implements Parcelable {
-
- @NonNull
- private final TaskFragmentInfo mTaskFragmentInfo;
-
- @NonNull
- private final SurfaceControl mLeash;
-
- /** @hide */
- public TaskFragmentAppearedInfo(
- @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) {
- mTaskFragmentInfo = taskFragmentInfo;
- mLeash = leash;
- }
-
- @NonNull
- public TaskFragmentInfo getTaskFragmentInfo() {
- return mTaskFragmentInfo;
- }
-
- @NonNull
- public SurfaceControl getLeash() {
- return mLeash;
- }
-
- private TaskFragmentAppearedInfo(Parcel in) {
- mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
- mLeash = in.readTypedObject(SurfaceControl.CREATOR);
- }
-
- /** @hide */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeTypedObject(mTaskFragmentInfo, flags);
- dest.writeTypedObject(mLeash, flags);
- }
-
- @NonNull
- public static final Creator<TaskFragmentAppearedInfo> CREATOR =
- new Creator<TaskFragmentAppearedInfo>() {
- @Override
- public TaskFragmentAppearedInfo createFromParcel(Parcel in) {
- return new TaskFragmentAppearedInfo(in);
- }
-
- @Override
- public TaskFragmentAppearedInfo[] newArray(int size) {
- return new TaskFragmentAppearedInfo[size];
- }
- };
-
- @Override
- public String toString() {
- return "TaskFragmentAppearedInfo{"
- + " taskFragmentInfo=" + mTaskFragmentInfo
- + "}";
- }
-
- /** @hide */
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 337c5a1..9c2fde0 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -120,8 +120,7 @@
}
/** Called when a TaskFragment is created and organized by this organizer. */
- public void onTaskFragmentAppeared(
- @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {}
/** Called when the status of an organized TaskFragment is changed. */
public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {}
@@ -169,7 +168,7 @@
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
mExecutor.execute(
() -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
}
@@ -217,4 +216,17 @@
return null;
}
}
+
+ /**
+ * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
+ * only occupies a portion of Task bounds.
+ * @hide
+ */
+ public boolean isActivityEmbedded(@NonNull IBinder activityToken) {
+ try {
+ return getController().isActivityEmbedded(activityToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 27c7d315..3ec18db 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -24,6 +24,7 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.app.TaskInfo.CameraCompatControlState;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
@@ -238,6 +239,20 @@
}
/**
+ * Updates a state of camera compat control for stretched issues in the viewfinder.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ public void updateCameraCompatControlState(@NonNull WindowContainerToken task,
+ @CameraCompatControlState int state) {
+ try {
+ mTaskOrganizerController.updateCameraCompatControlState(task, state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the executor to run callbacks on.
* @hide
*/
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index bbf8138..6864cca 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -368,10 +368,12 @@
*/
@NonNull
public WindowContainerTransaction setAdjacentRoots(
- @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
+ @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2,
+ boolean moveTogether) {
mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
root1.asBinder(),
- root2.asBinder()));
+ root2.asBinder(),
+ moveTogether));
return this;
}
@@ -975,6 +977,9 @@
private boolean mReparentTopOnly;
+ // TODO(b/207185041): Remove this once having a single-top root for split screen.
+ private boolean mMoveAdjacentTogether;
+
@Nullable
private int[] mWindowingModes;
@@ -1033,10 +1038,13 @@
.build();
}
- public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
+ /** Create a hierarchy op for setting adjacent root tasks. */
+ public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2,
+ boolean moveTogether) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
.setContainer(root1)
.setReparentContainer(root2)
+ .setMoveAdjacentTogether(moveTogether)
.build();
}
@@ -1070,6 +1078,7 @@
mReparent = copy.mReparent;
mToTop = copy.mToTop;
mReparentTopOnly = copy.mReparentTopOnly;
+ mMoveAdjacentTogether = copy.mMoveAdjacentTogether;
mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions;
@@ -1084,6 +1093,7 @@
mReparent = in.readStrongBinder();
mToTop = in.readBoolean();
mReparentTopOnly = in.readBoolean();
+ mMoveAdjacentTogether = in.readBoolean();
mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle();
@@ -1128,6 +1138,10 @@
return mReparentTopOnly;
}
+ public boolean getMoveAdjacentTogether() {
+ return mMoveAdjacentTogether;
+ }
+
public int[] getWindowingModes() {
return mWindowingModes;
}
@@ -1175,7 +1189,8 @@
return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
return "{SetAdjacentRoot: container=" + mContainer
- + " adjacentRoot=" + mReparent + "}";
+ + " adjacentRoot=" + mReparent + " mMoveAdjacentTogether="
+ + mMoveAdjacentTogether + "}";
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
return "{LaunchTask: " + mLaunchOptions + "}";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
@@ -1212,6 +1227,7 @@
dest.writeStrongBinder(mReparent);
dest.writeBoolean(mToTop);
dest.writeBoolean(mReparentTopOnly);
+ dest.writeBoolean(mMoveAdjacentTogether);
dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions);
@@ -1251,6 +1267,8 @@
private boolean mReparentTopOnly;
+ private boolean mMoveAdjacentTogether;
+
@Nullable
private int[] mWindowingModes;
@@ -1293,6 +1311,11 @@
return this;
}
+ Builder setMoveAdjacentTogether(boolean moveAdjacentTogether) {
+ mMoveAdjacentTogether = moveAdjacentTogether;
+ return this;
+ }
+
Builder setWindowingModes(@Nullable int[] windowingModes) {
mWindowingModes = windowingModes;
return this;
@@ -1336,6 +1359,7 @@
: null;
hierarchyOp.mToTop = mToTop;
hierarchyOp.mReparentTopOnly = mReparentTopOnly;
+ hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether;
hierarchyOp.mLaunchOptions = mLaunchOptions;
hierarchyOp.mActivityIntent = mActivityIntent;
hierarchyOp.mPendingIntent = mPendingIntent;
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 5aa6233..17b675f 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -19,13 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
import android.view.IWindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,7 +35,6 @@
* @hide
*/
public class WindowContextController {
- private final IWindowManager mWms;
/**
* {@code true} to indicate that the {@code mToken} is associated with a
* {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
@@ -56,14 +52,7 @@
* {@link Context#getWindowContextToken()}.
*/
public WindowContextController(@NonNull WindowTokenClient token) {
- this(token, WindowManagerGlobal.getWindowManagerService());
- }
-
- /** Used for test only. DO NOT USE it in production code. */
- @VisibleForTesting
- public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
mToken = token;
- mWms = mockWms;
}
/**
@@ -80,19 +69,7 @@
throw new IllegalStateException("A Window Context can be only attached to "
+ "a DisplayArea once.");
}
- try {
- final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
- displayId, options);
- if (configuration != null) {
- mAttachedToDisplayArea = true;
- // Send the DisplayArea's configuration to WindowContext directly instead of
- // waiting for dispatching from WMS.
- mToken.onConfigurationChanged(configuration, displayId,
- false /* shouldReportConfigChange */);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options);
}
/**
@@ -120,22 +97,14 @@
throw new IllegalStateException("The Window Context should have been attached"
+ " to a DisplayArea.");
}
- try {
- mWms.attachWindowContextToWindowToken(mToken, windowToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mToken.attachToWindowToken(windowToken);
}
/** Detaches the window context from the node it's currently associated with. */
public void detachIfNeeded() {
if (mAttachedToDisplayArea) {
- try {
- mWms.detachWindowContextFromWindowContainer(mToken);
- mAttachedToDisplayArea = false;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mToken.detachFromWindowContainerIfNeeded();
+ mAttachedToDisplayArea = false;
}
}
}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index f3e3859..4ba7ef2 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,12 +15,12 @@
*/
package android.window;
-import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
@@ -31,7 +31,11 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import android.view.IWindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
+import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,10 +63,14 @@
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
+ private IWindowManager mWms;
+
private final Configuration mConfiguration = new Configuration();
private boolean mShouldDumpConfigForIme;
+ private boolean mAttachToWindowContainer;
+
/**
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
@@ -84,6 +92,88 @@
}
/**
+ * Attaches this {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
+ *
+ * @param type The window type of the {@link WindowContext}
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @param options The window context launched option
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayArea(@WindowType int type, int displayId,
+ @Nullable Bundle options) {
+ try {
+ final Configuration configuration = getWindowManagerService()
+ .attachWindowContextToDisplayArea(this, type, displayId, options);
+ if (configuration == null) {
+ return false;
+ }
+ onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mAttachToWindowContainer = true;
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code DisplayContent}.
+ *
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayContent(int displayId) {
+ final IWindowManager wms = getWindowManagerService();
+ // #createSystemUiContext may call this method before WindowManagerService is initialized.
+ if (wms == null) {
+ return false;
+ }
+ try {
+ final Configuration configuration = wms.attachToDisplayContent(this, displayId);
+ if (configuration == null) {
+ return false;
+ }
+ onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mAttachToWindowContainer = true;
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
+ *
+ * @param windowToken the window token to associated with
+ */
+ public void attachToWindowToken(IBinder windowToken) {
+ try {
+ getWindowManagerService().attachWindowContextToWindowToken(this, windowToken);
+ mAttachToWindowContainer = true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Detaches this {@link WindowTokenClient} from associated WindowContainer if there's one. */
+ public void detachFromWindowContainerIfNeeded() {
+ if (!mAttachToWindowContainer) {
+ return;
+ }
+ try {
+ getWindowManagerService().detachWindowContextFromWindowContainer(this);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private IWindowManager getWindowManagerService() {
+ if (mWms == null) {
+ mWms = WindowManagerGlobal.getWindowManagerService();
+ }
+ return mWms;
+ }
+
+ /**
* Called when {@link Configuration} updates from the server side receive.
*
* @param newConfig the updated {@link Configuration}
@@ -131,14 +221,7 @@
() -> windowContext.dispatchConfigurationChanged(newConfig));
}
- // Dispatch onConfigurationChanged only if there's a significant public change to
- // make it compatible with the original behavior.
- final Configuration[] sizeConfigurations = context.getResources()
- .getSizeConfigurations();
- final SizeConfigurationBuckets buckets = sizeConfigurations != null
- ? new SizeConfigurationBuckets(sizeConfigurations) : null;
- final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
-
+ final int diff = mConfiguration.diffPublicOnly(newConfig);
if (shouldReportConfigChange && diff != 0
&& context instanceof WindowProviderService) {
final WindowProviderService windowProviderService = (WindowProviderService) context;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 786af5f..7bb1ed8 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -163,9 +163,6 @@
private AppPredictor mWorkAppPredictor;
private boolean mShouldDisplayLandscape;
- private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
- private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
-
@UnsupportedAppUsage
public ChooserActivity() {
}
@@ -275,6 +272,7 @@
private int mCurrAvailableWidth = 0;
private int mLastNumberOfChildren = -1;
+ private int mMaxTargetsPerRow = 1;
private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
@@ -741,8 +739,9 @@
mCallerChooserTargets = targets;
}
- mShouldDisplayLandscape = shouldDisplayLandscape(
- getResources().getConfiguration().orientation);
+ mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
+ mShouldDisplayLandscape =
+ shouldDisplayLandscape(getResources().getConfiguration().orientation);
setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false));
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false);
@@ -916,7 +915,7 @@
adapter,
getPersonalProfileUserHandle(),
/* workProfileUserHandle= */ null,
- isSendAction(getTargetIntent()), getMaxTargetsPerRow());
+ isSendAction(getTargetIntent()), mMaxTargetsPerRow);
}
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
@@ -945,7 +944,7 @@
selectedProfile,
getPersonalProfileUserHandle(),
getWorkProfileUserHandle(),
- isSendAction(getTargetIntent()), getMaxTargetsPerRow());
+ isSendAction(getTargetIntent()), mMaxTargetsPerRow);
}
private int findSelectedProfile() {
@@ -1107,6 +1106,7 @@
}
mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
+ mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
adjustPreviewWidth(newConfig.orientation, null);
updateStickyContentPreview();
}
@@ -2690,7 +2690,7 @@
// and b/150936654
recyclerView.setAdapter(gridAdapter);
((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(
- getMaxTargetsPerRow());
+ mMaxTargetsPerRow);
}
UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle();
@@ -2855,7 +2855,7 @@
@Override // ChooserListCommunicator
public int getMaxRankedTargets() {
- return getMaxTargetsPerRow();
+ return mMaxTargetsPerRow;
}
@Override // ChooserListCommunicator
@@ -3203,13 +3203,6 @@
}
}
- int getMaxTargetsPerRow() {
- int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
- if (mShouldDisplayLandscape) {
- maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
- }
- return maxTargets;
- }
/**
* Adapter for all types of items and targets in ShareSheet.
* Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
@@ -3277,7 +3270,11 @@
return false;
}
- int newWidth = width / getMaxTargetsPerRow();
+ // Limit width to the maximum width of the chooser activity
+ int maxWidth = getResources().getDimensionPixelSize(R.dimen.chooser_width);
+ width = Math.min(maxWidth, width);
+
+ int newWidth = width / mMaxTargetsPerRow;
if (newWidth != mChooserTargetWidth) {
mChooserTargetWidth = newWidth;
return true;
@@ -3312,7 +3309,7 @@
+ getAzLabelRowCount()
+ Math.ceil(
(float) mChooserListAdapter.getAlphaTargetCount()
- / getMaxTargetsPerRow())
+ / mMaxTargetsPerRow)
);
}
@@ -3352,7 +3349,7 @@
public int getCallerAndRankedTargetRowCount() {
return (int) Math.ceil(
((float) mChooserListAdapter.getCallerTargetCount()
- + mChooserListAdapter.getRankedTargetCount()) / getMaxTargetsPerRow());
+ + mChooserListAdapter.getRankedTargetCount()) / mMaxTargetsPerRow);
}
// There can be at most one row in the listview, that is internally
@@ -3551,7 +3548,7 @@
parentGroup.addView(row2);
mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
- Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType);
+ Lists.newArrayList(row1, row2), mMaxTargetsPerRow, viewType);
loadViewsIntoGroup(mDirectShareViewHolder);
return mDirectShareViewHolder;
@@ -3559,7 +3556,7 @@
ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
false);
ItemGroupViewHolder holder =
- new SingleRowViewHolder(row, getMaxTargetsPerRow(), viewType);
+ new SingleRowViewHolder(row, mMaxTargetsPerRow, viewType);
loadViewsIntoGroup(holder);
return holder;
@@ -3651,7 +3648,7 @@
final int serviceCount = mChooserListAdapter.getServiceTargetCount();
final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets());
if (position < serviceRows) {
- return position * getMaxTargetsPerRow();
+ return position * mMaxTargetsPerRow;
}
position -= serviceRows;
@@ -3660,7 +3657,7 @@
+ mChooserListAdapter.getRankedTargetCount();
final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
if (position < callerAndRankedRows) {
- return serviceCount + position * getMaxTargetsPerRow();
+ return serviceCount + position * mMaxTargetsPerRow;
}
position -= getAzLabelRowCount() + callerAndRankedRows;
@@ -3673,7 +3670,7 @@
if (mDirectShareViewHolder != null && canExpandDirectShare) {
mDirectShareViewHolder.handleScroll(
mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy,
- getMaxTargetsPerRow());
+ mMaxTargetsPerRow);
}
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 424632fe..6541b14 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -270,8 +270,6 @@
private Drawable mCaptionBackgroundDrawable;
private Drawable mUserCaptionBackgroundDrawable;
- private float mAvailableWidth;
-
String mLogTag = TAG;
private final Rect mFloatingInsets = new Rect();
private boolean mApplyFloatingVerticalInsets = false;
@@ -315,8 +313,6 @@
mSemiTransparentBarColor = context.getResources().getColor(
R.color.system_bar_background_semi_transparent, null /* theme */);
- updateAvailableWidth();
-
setWindow(window);
updateLogTag(params);
@@ -697,7 +693,8 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+ final Resources res = getContext().getResources();
+ final DisplayMetrics metrics = res.getDisplayMetrics();
final boolean isPortrait =
getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
@@ -767,17 +764,19 @@
if (!fixedWidth && widthMode == AT_MOST) {
final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
+ final float availableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ res.getConfiguration().screenWidthDp, metrics);
if (tv.type != TypedValue.TYPE_NULL) {
final int min;
if (tv.type == TypedValue.TYPE_DIMENSION) {
- min = (int)tv.getDimension(metrics);
+ min = (int) tv.getDimension(metrics);
} else if (tv.type == TypedValue.TYPE_FRACTION) {
- min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
+ min = (int) tv.getFraction(availableWidth, availableWidth);
} else {
min = 0;
}
if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::"
- + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth);
+ + tv.coerceToString() + ", mAvailableWidth=" + availableWidth);
if (width < min) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
@@ -2144,7 +2143,6 @@
updateDecorCaptionStatus(newConfig);
- updateAvailableWidth();
initializeElevation();
}
@@ -2616,12 +2614,6 @@
mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
}
- private void updateAvailableWidth() {
- Resources res = getResources();
- mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
- }
-
/**
* @hide
*/
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index c1587eb..1382a99 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -67,21 +67,19 @@
}
};
-static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
- jlong width, jlong height, jint format) {
- String8 str8;
- if (jName) {
- const jchar* str16 = env->GetStringCritical(jName, nullptr);
- if (str16) {
- str8 = String8(reinterpret_cast<const char16_t*>(str16), env->GetStringLength(jName));
- env->ReleaseStringCritical(jName, str16);
- str16 = nullptr;
- }
- }
- std::string name = str8.string();
+static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName) {
+ ScopedUtfChars name(env, jName);
+ sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str());
+ queue->incStrong((void*)nativeCreate);
+ return reinterpret_cast<jlong>(queue.get());
+}
+
+static jlong nativeCreateAndUpdate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
+ jlong width, jlong height, jint format) {
+ ScopedUtfChars name(env, jName);
sp<BLASTBufferQueue> queue =
- new BLASTBufferQueue(name, reinterpret_cast<SurfaceControl*>(surfaceControl), width,
- height, format);
+ new BLASTBufferQueue(name.c_str(), reinterpret_cast<SurfaceControl*>(surfaceControl),
+ width, height, format);
queue->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(queue.get());
}
@@ -134,10 +132,16 @@
}
}
+static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ return queue->getLastAcquiredFrameNum();
+}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
- {"nativeCreate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreate},
+ {"nativeCreate", "(Ljava/lang/String;)J", (void*)nativeCreate},
+ {"nativeCreateAndUpdate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreateAndUpdate},
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
@@ -145,7 +149,8 @@
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeSetTransactionCompleteCallback",
"(JJLandroid/graphics/BLASTBufferQueue$TransactionCompleteCallback;)V",
- (void*)nativeSetTransactionCompleteCallback}
+ (void*)nativeSetTransactionCompleteCallback},
+ {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
// clang-format on
};
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 4c2b114..5e0d9b3 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -34,6 +34,7 @@
#include <vector>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <bionic/malloc.h>
#include <debuggerd/client.h>
#include <log/log.h>
@@ -859,7 +860,22 @@
return poolsSizeKb;
}
+static bool halSupportsGpuPrivateMemory() {
+ int productApiLevel =
+ android::base::GetIntProperty("ro.product.first_api_level",
+ android::base::GetIntProperty("ro.build.version.sdk",
+ __ANDROID_API_FUTURE__));
+ int boardApiLevel =
+ android::base::GetIntProperty("ro.board.api_level",
+ android::base::GetIntProperty("ro.board.first_api_level",
+ __ANDROID_API_FUTURE__));
+
+ return std::min(productApiLevel, boardApiLevel) >= __ANDROID_API_S__;
+}
+
static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz) {
+ static bool gpuPrivateMemorySupported = halSupportsGpuPrivateMemory();
+
struct memtrack_proc* p = memtrack_proc_new();
if (p == nullptr) {
LOG(ERROR) << "getGpuPrivateMemoryKb: Failed to create memtrack_proc";
@@ -876,6 +892,12 @@
ssize_t gpuPrivateMem = memtrack_proc_gl_pss(p);
memtrack_proc_destroy(p);
+
+ // Old HAL implementations may return 0 for GPU private memory if not supported
+ if (gpuPrivateMem == 0 && !gpuPrivateMemorySupported) {
+ return -1;
+ }
+
return gpuPrivateMem / 1024;
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c750ead..6faa046 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -366,6 +366,7 @@
optional bool pip_auto_enter_enabled = 31;
optional bool in_size_compat_mode = 32;
optional float min_aspect_ratio = 33;
+ optional bool provides_max_bounds = 34;
}
/* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3968193..f525315 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5681,6 +5681,10 @@
<!-- Allows input events to be monitored. Very dangerous! @hide -->
<permission android:name="android.permission.MONITOR_INPUT"
android:protectionLevel="signature|recents" />
+ <!-- Allows the use of FLAG_SLIPPERY, which permits touch events to slip from the current
+ window to the window where the touch currently is on top of. @hide -->
+ <permission android:name="android.permission.ALLOW_SLIPPERY_TOUCHES"
+ android:protectionLevel="signature|recents" />
<!-- Allows the caller to change the associations between input devices and displays.
Very dangerous! @hide -->
<permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY"
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 90caacc..933b4d2 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -20,8 +20,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_gravity="center"
android:maxCollapsedHeight="0dp"
android:maxCollapsedHeightSmall="56dp"
+ android:maxWidth="@dimen/chooser_width"
android:id="@id/contentPanel">
<RelativeLayout
diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml
index 0d04d7f3..52692b0 100644
--- a/core/res/res/layout/chooser_grid_preview_image.xml
+++ b/core/res/res/layout/chooser_grid_preview_image.xml
@@ -34,7 +34,7 @@
<view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
android:id="@+id/content_preview_image_1_large"
android:layout_width="120dp"
- android:layout_height="140dp"
+ android:layout_height="104dp"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:gravity="center"
@@ -44,7 +44,7 @@
android:id="@+id/content_preview_image_2_large"
android:visibility="gone"
android:layout_width="120dp"
- android:layout_height="140dp"
+ android:layout_height="104dp"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/content_preview_image_1_large"
android:layout_marginLeft="10dp"
diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml
index 2b9f952..304affe 100644
--- a/core/res/res/layout/splash_screen_view.xml
+++ b/core/res/res/layout/splash_screen_view.xml
@@ -36,6 +36,7 @@
android:layout_marginBottom="60dp"
android:padding="0dp"
android:background="@null"
+ android:forceHasOverlappingRendering="false"
android:contentDescription="@string/splash_screen_view_branding_description"/>
</android.window.SplashScreenView>
\ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 7763416..fe2d573 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tik om jou gesigmodel uit te vee en voeg jou gesig dan weer by"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Stel Gesigslot op"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontsluit jou foon deur daarna te kyk"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Skakel "<b>"kameratoegang"</b>" in Instellings > Privaatheid aan om Gesigslot te gebruik"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer maniere op om te ontsluit"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om \'n vingerafdruk by te voeg"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Vingerafdrukslot"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor pogings om skerm te ontsluit"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor die aantal keer wat \'n verkeerde wagwoorde ingevoer is wanneer die skerm ontsluit word. Sluit die tablet of vee al die data uit as die wagwoord te veel keer verkeerd ingevoer word."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor die aantal verkeerde wagwoorde wat ingetik word wanneer die skerm ontsluit word, en sluit jou Android TV-toestel of vee al jou Android TV-toestel se data uit as te veel verkeerde wagwoorde ingetik word."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor die aantal verkeerde wagwoorde wat ingevoer word wanneer die skerm ontsluit word, en sluit die inligtingvermaakstelsel of vee al die inligtingvermaakstelsel se data uit as te veel verkeerde wagwoorde ingevoer word."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor die aantal keer wat \'n verkeerde wagwoorde ingevoer is wanneer die skerm ontsluit word. Sluit die foon of vee al die data uit as die wagwoord te veel keer verkeerd ingevoer word."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor die aantal verkeerde wagwoorde wat ingevoer word wanneer die skerm ontsluit word, en sluit die tablet of vee al hierdie gebruiker se data uit as te veel verkeerde wagwoorde ingevoer word."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor die aantal verkeerde wagwoorde wat ingetik word wanneer die skerm ontsluit word, en sluit jou Android TV-toestel of vee al hierdie gebruiker se data uit as hulle te veel verkeerde wagwoorde intik."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor die aantal verkeerde wagwoorde wat ingevoer word wanneer die skerm ontsluit word, en sluit die inligtingvermaakstelsel of vee al hierdie profiel se data uit as te veel verkeerde wagwoorde ingevoer word."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor die aantal verkeerde wagwoorde wat ingevoer word wanneer die skerm ontsluit word, en sluit die foon of vee al hierdie gebruiker se data uit as te veel verkeerde wagwoorde ingevoer word."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Om die skermslot te verander"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Verander die skermslot."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Om alle data uit te vee"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Vee die tablet se data uit sonder waarskuwing, deur \'n fabrieksterugstelling uit te voer."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Vee jou Android TV-toestel se data sonder waarskuwing uit deur \'n fabrieksterugstelling uit te voer."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Vee die inligtingvermaakstelsel se data sonder waarskuwing uit deur \'n fabriekterugstelling te doen."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Vee die foon se data uit sonder waarskuwing, deur \'n fabrieksterugstelling uit te voer."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Vee gebruikerdata uit"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Vee profieldata uit"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Vee gebruikerdata uit"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Vee hierdie gebruiker se data in hierdie tablet sonder waarskuwing uit."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Vee hierdie gebruiker se data op hierdie Android TV-toestel sonder waarskuwing uit."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Vee hierdie profiel se data op hierdie inligtingvermaakstelsel sonder waarskuwing uit."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Vee hierdie gebruiker se data in hierdie foon sonder waarskuwing uit."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Stel die toestel se globale instaan"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Stel die toestel se globale instaanbediener wat gebruik moet word terwyl die beleid geaktiveer is. Net die toesteladministrateur kan die globale instaanbediener stel."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 1c306403a..f9576cea 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"የእርስዎ የመልክ ሞዴል ለመሰረዝ መታ ያድርጉ፣ ከዚያ መልክዎን እንደገና ያክሉ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"በመልክ መክፈትን ያዋቅሩ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ስልክዎን በመመልከት ያስከፍቱት"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"በመልክ መክፈትን ለመጠቀም "<b>"የካሜራ መዳረሻ"</b>"ን በቅንብሮች እና ግላዊነት ውስጥ ያብሩ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"የሚከፍቱባቸው ተጨማሪ መንገዶችን ያቀናብሩ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"የጣት አሻራን ለማከል መታ ያድርጉ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"በጣት አሻራ መክፈቻ"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"የማሳያ-ክፈት ሙከራዎችን ክትትል ያድርጉባቸው"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ማሳያውን በምትከፍትበት ጊዜ በስህተት የተተየቡ የይለፍ ቃሎችን ቁጥር ተቆጣጠር፤ እና ጡባዊ ተኮውን ቆልፍ ወይም በጣም ብዙ የተሳሳቱ የይለፍ ቃሎች ከተተየቡ የጡባዊ ተኮን ውሂብ አጥፋ፡፡"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ማያ ገጹን ሲከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ የእርስዎን Android TV ን ቆልፍ ወይም ሁሉንም የእርስዎን Android TV ደምስስ።"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ የኢንፎቴይንመንት ስርዓቱን ቆልፍ ወይም ሁሉንም የኢንፎቴይንመንት ስርዓት ውሂብ ደምስስ።"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"የተተየቡ ልክ ያልሆኑ የይለፍ ቃሎችን ቁጥር ተቆጣጠር፡፡ማሳያውን በምትከፍትበት ጊዜ፤ እና በጣም ብዙ ልክ ያልሆኑ የይለፍ ቃሎች ከተተየቡ ስልኩን ቆልፈው ወይም ሁሉንም የስልኩን ውሂብ ደምስሰው፡፡"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ ጡባዊውን ቆልፍ ወይም ሁሉንም የዚህን ተጠቃሚ ውሂብ ደምስስ።."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ የእርስዎ Android TV መሣሪያን ቆልፍ ወይም ሁሉንም የዚህን ተጠቃሚ ውሂብ ደምስስ።"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ የኢንፎቴይንመንት ስርዓቱን ቆልፍ ወይም ሁሉንም የዚህን ተጠቃሚ ውሂብ ደምስስ።"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ ስልኩን ቆልፍ ወይም ሁሉንም የዚህን ተጠቃሚ ውሂብ ደምስስ።"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"የማያ ገጹን መቆለፊያ መለወጥ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"የማያ ገጽ መቆለፊያውን ለውጥ።"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ሁሉንም ውሂብ መሰረዝ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"የፋብሪካው ውሂብ ዳግም አስጀምርን በማከናወን፣ያለ ማስጠንቀቂያ የጡባዊውን ውሂብ አጥፋ።"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"የፋብሪካ ውሂብ ዳግም ቅንብርን ያለ ማስጠንቀቂያ በማከናወን የእርስዎን Android TV መሣሪያ ውሂብን ደምስስ።"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"የፋብሪካ ውሂብ ዳግም ማስጀመር በማከናወን ያለማስጠንቀቂያ የኢንፎቴይንመንት ስርዓትን ውሂብ ደምስስ።"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"የፋብሪካ ውሂብ ድጋሚ አስጀምር በማከናወን ያለ ማሰጠንቀቂያ የስልኩን ውሂብ ደምስስ።"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"የተጠቃሚ ውሂብ ደምስስ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"የመገለጫ ውሂብ ደምስስ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"የተጠቃሚ ውሂብ ደምስስ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ያለምንም ማስጠንቀቂያ የዚህን ጡባዊ የተጠቃሚ ውሂብ ደምስስ።"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"በዚህ Android TV መሣሪያ ላይ ያለ ማስጠንቀቂያ የዚህን ተጠቃሚ ውሂብ ደምስስ።"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"በዚህ የኢንፎቴይንመንት ሥርዓት ላይ ያለ ማስጠንቀቂያ የዚህን መገለጫ ውሂብ ደምስስ።"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ያለምንም ማስጠንቀቂያ የዚህን ስልክ የተጠቃሚ ውሂብ ደምስስ።"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"የመሣሪያውን ሁሉንም ፕሮክሲ አዘጋጅ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"መመሪያ ነቅቶ እያለ ጥቅም ላይ ሊውል የሚችለውን የመሣሪያውን ሁሉንተናዊ ተኪ አዘጋጅ። የመሣሪያ ባለቤት ብቻ የሁሉንተናዊ ተኪውን ማዘጋጀት ይችላል።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 284c0e3..acf7d39 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -287,7 +287,7 @@
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
<string name="notification_hidden_text" msgid="2835519769868187223">"إشعار جديد"</string>
<string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"لوحة المفاتيح الافتراضية"</string>
- <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"لوحة المفاتيح الفعلية"</string>
+ <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"لوحة المفاتيح الخارجية"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"الأمان"</string>
<string name="notification_channel_car_mode" msgid="2123919247040988436">"وضع السيارة"</string>
<string name="notification_channel_account" msgid="6436294521740148173">"حالة الحساب"</string>
@@ -628,6 +628,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"انقر لحذف نموذج الوجه ثم أضِف نموذجًا لوجهك مرة أخرى."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"إعداد ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"يمكنك فتح قفل هاتفك بمجرّد النظر إلى الشاشة."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"لاستخدام ميزة \"فتح الجهاز بالتعرف على الوجه\"، عليك منح إذن "<b>"الوصول إلى الكاميرا"</b>" في الإعدادات > الخصوصية."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"إعداد المزيد من الطرق لفتح قفل الجهاز"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"انقر لإضافة بصمة إصبع."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فتح الجهاز ببصمة الإصبع"</string>
@@ -741,9 +742,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"مراقبة محاولات فتح قفل الشاشة"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"لمراقبة عدد مرات كتابة كلمات المرور غير الصحيحة عند فتح قفل الشاشة وتأمين الجهاز اللوحي أو مسح جميع بياناته في حالة كتابة الكثير من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وقفل جهاز Android TV أو محو جميع بيانات جهاز Android TV إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وقفل \"نظام الترفيه والمعلومات\" أو محو جميع بياناته إذا تمت كتابة عدد كبير جدًا من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وتأمين الهاتف ومحو جميع بياناته إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وتأمين الجهاز اللوحي ومحو جميع بيانات هذا المستخدم إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وقفل جهاز Android TV أو محو جميع بيانات هذا المستخدم إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وتأمين \"نظام الترفيه والمعلومات\" ومحو جميع بيانات هذا الملف الشخصي إذا تمت كتابة عدد كبير جدًا من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وتأمين الهاتف ومحو جميع بيانات هذا المستخدم إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"تغيير قفل الشاشة"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"إمكانية تغيير قفل الشاشة"</string>
@@ -752,10 +755,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"محو جميع البيانات"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"يمكنك محو بيانات الجهاز اللوحي بدون تحذير، وذلك عبر إجراء إعادة الضبط على الإعدادات الأصلية."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"يمكنك محو بيانات جهاز Android TV بدون تحذير عن طريق تنفيذ إعادة الضبط على الإعدادات الأصلية."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"يمكنك محو بيانات \"نظام الترفيه والمعلومات\" بدون تحذير، وذلك من خلال إعادة الضبط على الإعدادات الأصلية."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"محو بيانات الهاتف بدون تحذير، وذلك من خلال إعادة ضبط البيانات على الإعدادات الأصلية"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"محو بيانات المستخدم"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"محو بيانات الملف الشخصي"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"محو بيانات المستخدم"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"لمحو بيانات هذا المستخدم على هذا الجهاز اللوحي بدون تحذير."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"لمحو بيانات هذا المستخدم على جهاز Android TV هذا بدون تحذير."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"يمكنك محو بيانات هذا الملف الشخصي على \"نظام الترفيه والمعلومات\" هذا بدون تحذير."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"لمحو بيانات هذا المستخدم على هذا الهاتف بدون تحذير."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"تعيين الخادم الوكيل العمومي للجهاز"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"لضبط الخادم الوكيل العام في الجهاز على الاستخدام أثناء تفعيل السياسة. ولن يمكن لأحد سوى مالك الجهاز ضبط الخادم الوكيل العام."</string>
@@ -1713,7 +1719,7 @@
<string name="default_audio_route_category_name" msgid="5241740395748134483">"النظام"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"صوت بلوتوث"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"عرض شاشة لاسلكي"</string>
- <string name="media_route_button_content_description" msgid="2299223698196869956">"إرسال"</string>
+ <string name="media_route_button_content_description" msgid="2299223698196869956">"البث"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"الاتصال بجهاز"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"بث الشاشة على الجهاز"</string>
<string name="media_route_chooser_searching" msgid="6119673534251329535">"جارٍ البحث عن الأجهزة…"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 683b093..ae62c70 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপোনাৰ মুখাৱয়বৰ মডেলটো মচিবলৈ টিপক, তাৰ পাছত পুনৰ আপোনাৰ মুখাৱয়ব যোগ দিয়ক"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ফেচ আনলক সুবিধাটো ছেট আপ কৰক"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপোনাৰ ফ’নটোলৈ চাই সেইটো আনলক কৰক"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ফেচ আনলক সুবিধাটো ব্যৱহাৰ কৰিবলৈ ছেটিং > গোপনীয়তাত "<b>"কেমেৰাৰ এক্সেছ"</b>" অন কৰক"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক কৰাৰ অধিক উপায় ছেট আপ কৰক"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"এটা ফিংগাৰপ্ৰিণ্ট যোগ দিবলৈ টিপক"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ফিংগাৰপ্ৰিন্ট আনলক"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্ৰীন আনলক কৰা প্ৰয়াসবোৰ নিৰীক্ষণ কৰক"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে টেবলেটটো লক কৰক বা টেবলেটটোৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে Android TV ডিভাইচটো লক কৰক অথবা আপোনাৰ Android TV ডিভাইচৰ আটাইখিনি ডেটা মচক।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"স্ক্ৰীন আনলক কৰোঁতে দিয়া অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড দিয়া হয় তেন্তে ইনফ’টেইনমেণ্ট ছিষ্টেমটো লক কৰক অথবা এই ইনফ’টেইনমেণ্ট ছিষ্টেমটোৰ আটাইবোৰ ডেটা মোহাৰক।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে ফ\'নটো লক কৰক বা ফ\'নটোৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে টেবলেটটো লক কৰক বা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"স্ক্ৰীনখন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে Android TV ডিভাইচটো লক কৰক অথবা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"স্ক্ৰীন আনলক কৰোঁতে দিয়া অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড দিয়া হয় তেন্তে ইনফ’টেইনমেণ্ট ছিষ্টেমটো লক কৰক অথবা এই প্ৰ’ফাইলটোৰ আটাইবোৰ ডেটা মোহাৰক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"স্ক্ৰীনখন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে ফ\'নটো লক কৰক অথবা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"স্ক্ৰীন লক সলনি কৰক"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"স্ক্ৰীন লক সলনি কৰক।"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"আটাইবোৰ ডেটা মচক"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"সতৰ্কবাণী প্ৰেৰণ নকৰাকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি টেবলেটৰ ডেটা মচক।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"কোনো সতর্কবার্তা নপঠিওৱাকৈ ফেক্টৰী ডেটা ৰিছেট কৰি আপোনাৰ Android TV ডিভাইচৰ ডেটা মচক।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"সর্তকবাণী নিদিয়াকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি ইনফ’টেইনমেণ্ট ছিষ্টেমৰ ডেটা মোহাৰক।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"সতৰ্কবাণী প্ৰেৰণ নকৰাকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি ফ\'নৰ ডেটা মচক।"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ব্য়ৱহাৰকাৰীৰ তথ্য় মচক"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"প্ৰ’ফাইলৰ ডেটা মোহাৰক"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ব্য়ৱহাৰকাৰীৰ তথ্য় মচক"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"এই টেবলেটটোত থকা এই ব্যৱহাৰকাৰীৰ তথ্য কোনো সর্তকবাণী নিদিয়াকৈ মচি পেলাওক।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"কোনো সতর্কবার্তা নপঠিওৱাকৈ এই Android TV ডিভাইচটোত এই ব্যৱহাৰকাৰীৰ ডেটা মচক।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"সর্তকবাণী নিদিয়াকৈয়ে এই ইনফ’টেইনমেণ্ট ছিষ্টেমত এই প্ৰ’ফাইলটোৰ ডেটা মোহাৰক।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"এই ফ\'নটোত থকা এই ব্যৱহাৰকাৰীৰ তথ্য কোনো সর্তকবাণী নিদিয়াকৈ মচি পেলাওক।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ডিভাইচৰ বাবে গ্ল\'বেল প্ৰক্সী ছেট কৰক"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"নীতি সক্ষম কৰি থোৱা অৱস্থাত ব্য়ৱহাৰ কৰিবৰ বাবে ডিভাইচৰ বাবে গ্ল\'বেল প্ৰক্সী ছেট কৰক। কেৱল ডিভাইচৰ গৰাকীয়েহে গ্ল\'বেল প্ৰক্সী ছেট কৰিব পাৰে।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 4825382..b622364 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Üz modelinizi silmək üçün toxunun, sonra yenidən üzünüzü əlavə edin"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Üz ilə kiliddən çıxarmanı ayarlayın"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefona baxaraq onu kiliddən çıxarın"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Üz ilə Kiliddən Açma funksiyasını istifadə etmək üçün Ayarlar > Məxfilik bölməsində "<b>"Kameraya girişi"</b>" aktiv edin"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kiliddən çıxarmağın daha çox yolunu ayarlayın"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Barmaq izi əlavə etmək üçün toxunun"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Barmaq izi ilə kiliddən çıxarma"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranı kiliddən çıxarmaq üçün edilən cəhdlərə nəzarət edin"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Ekan kilidini açarkən daxil edilmiş yanlış parollara baxın və əgər həddindən çox yanlış parollar daxil edilibsə, planşeti kilidləyin və ya bütün planşet datasını silin."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin, Android TV cihazını kilidləyin və ya həddən çox yanlış parol yazılıbsa, Android TV cihazının bütün datasını silin."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin və həddindən çox yanlış parol daxil edilibsə, əyləncə-məlumat sistemini kilidləyin və ya bütün əyləncə-məlumat sistemi datasını silin."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Ekan kilidini açarkən daxil edilmiş yanlış parollara baxın və əgər həddindən çox yanlış parollar daxil edilibsə, telefonu kilidləyin və ya bütün telefon datasını silin."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin və planşeti kilidləyin və ya əgər həddən çox yanlış parol yazılıbsa, həmin istifadəçinin bütün verilənlərini silin."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin, Android TV cihazını kilidləyin və ya həddən çox yanlış parol yazılıbsa, həmin istifadəçinin bütün datasını silin."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin və həddindən çox yanlış parol daxil edilibsə, əyləncə-məlumat sistemini kilidləyin və ya bu profilin bütün datasını silin."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin və telefonu kilidləyin və ya əgər həddən çox yanlış parol yazılıbsa, həmin istifadəçinin bütün verilənlərini silin."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ekran kilidini dəyişmək"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ekran kilidini dəyişmək"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Bütün məlumatları silmək"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Planşetin datasını xəbərdarlıq olmadan, zavod data sıfırlaması ilə silin."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Android TV cihazının datasını fabrik sıfırlaması haqqında xəbərdarlıq olmadan silin."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Xəbərdarlıq etmədən istehsalçı nizamlarına qaytarmaqla əyləncə-məlumat sistemi datasını silin."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Telefondakı bütün məlumatları xəbərdarlıqsız sıfırlayaraq məhv etmək"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"İstifadəçi verilənlərini sil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profil datasını silin"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"İstifadəçi verilənlərini sil"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Xəbərdarlıq etmədən bu istifadəçinin verilənlərini bu planşetdə silin."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Bu istifadəçinin datasını xəbərdarlıq olmadan Android TV cihazında silin."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Xəbərdarlıq etmədən bu əyləncə-məlumat sistemində bu profilin datasını silin."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Xəbərdarlıq etmədən bu istifadəçinin bu telefondakı verilənlərini silin."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Cihazın qlobal proksisini ayarlayın"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Siyasət aktivləşdirilən zaman istifadə edilmək üçün cihazın qlobal proksisini təyin edin. Yalnız cihazın sahibi qlobal proksini təyin edə bilər."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 014215a..9b172c0 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -619,6 +619,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, pa ponovo dodajte svoje lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Podesite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon tako što ćete ga pogledati"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite "<b>"pristup kameri"</b>" u odeljku Podešavanja > Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Podesite još načina za otključavanje"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -732,9 +733,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadgledajte pokušaje otključavanja ekrana"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Prati broj netačno unetih lozinki prilikom otključavanja ekrana i zaključava tablet ili briše podatke sa tableta ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava Android TV uređaj ili briše sve podatke sa Android TV uređaja ako se unese previše netačnih lozinki."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava sistem za info-zabavu ili briše sve podatke sa sistema za info-zabavu ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava telefon ili briše sve podatke sa telefona ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava tablet ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava Android TV uređaj ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava sistem za info-zabavu ili briše sve podatke ovog profila ako se unese previše netačnih lozinki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava telefon ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Promena zaključavanja ekrana"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Menja zaključavanje ekrana."</string>
@@ -743,10 +746,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje svih podataka"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Brisanje podataka na tabletu bez upozorenja resetovanjem na fabrička podešavanja."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Briše podatke Android TV uređaja bez upozorenja pomoću resetovanja na fabrička podešavanja."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Briše podatke na sistemu za info-zabavu bez upozorenja resetovanjem na fabrička podešavanja."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Brisanje podataka na telefonu bez upozorenja resetovanjem na fabrička podešavanja."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Obriši podatke korisnika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Brisanje podataka profila"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Obriši podatke korisnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Briše podatke ovog korisnika na ovom tabletu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Briše podatke ovog korisnika na ovom Android TV uređaju bez upozorenja."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Briše podatke ovog profila na ovom sistemu za info-zabavu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Briše podatke ovog korisnika na ovom telefonu bez upozorenja."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Podesite globalni proksi server uređaja"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Podešava globalni proksi uređaja koji će se koristiti dok su smernice omogućene. Samo vlasnik uređaja može da podesi globalni proksi."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index d0491ce..4d9f157 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -402,7 +402,7 @@
<string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу планшэта."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Дазваляе праграме пастаянна захоўваць некаторыя свае часткі ў памяці прылады. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых праграм, і запаволіць працу прылады Android TV."</string>
<string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу тэлефона."</string>
- <string name="permlab_foregroundService" msgid="1768855976818467491">"запусціць асноўныя сэрвісы"</string>
+ <string name="permlab_foregroundService" msgid="1768855976818467491">"запусціць актыўныя сэрвісы"</string>
<string name="permdesc_foregroundService" msgid="8720071450020922795">"Дазваляе праграме выкарыстоўваць асноўныя сэрвісы."</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"вымерыць прастору для захоўвання прыкладання"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"Дазваляе прыкладанням атрымліваць яго код, дадзеныя і аб\'ём кэш-памяці"</string>
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Націсніце, каб выдаліць мадэль твару, пасля дадайце твар яшчэ раз"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Наладзьце распазнаванне твару"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Разблакіруйце свой тэлефон, паглядзеўшы на яго"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Каб выкарыстоўваць распазнаванне твару, уключыце "<b>"доступ да камеры"</b>" праз раздзел \"Налады > Прыватнасць\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Наладзьце дадатковыя спосабы разблакіроўкі"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Націсніце, каб дадаць адбітак пальца"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Разблакіраванне адбіткам пальца"</string>
@@ -720,8 +721,8 @@
<string name="permdesc_handoverStatus" msgid="3842269451732571070">"Дазваляе праграме атрымліваць інфармацыю аб бягучых перадачах Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="710576248717404416">"выдаленне сертыфікатаў DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Дазваляе праграме выдаляць сертыфікаты DRM. Ніколі не павінна патрабавацца для звычайных праграм."</string>
- <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"прывязка да службы паведамленняў аператара"</string>
- <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Дазваляе ўладальніку выконваць прывязку да інтэрфейсу верхняга ўзроўню службы паведамленняў аператара. Ніколі не павінна патрабавацца для звычайных праграм."</string>
+ <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"падключэнне да сэрвісу абмену паведамленнямі аператара"</string>
+ <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Дазваляе ўладальніку выконваць падключэнне да базавага інтэрфейсу сэрвісу абмену паведамленнямі аператара. Звычайныя праграмы ніколі не выкарыстоўваюць гэты дазвол."</string>
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"прывязвацца з сэрвісаў аператара"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Дазваляе ўладальніку ажыццяўляць прывязку да сэрвісаў аператара. Ніколі не павінна патрабавацца для звычайных праграм."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"атрымліваць доступ да рэжыму «Не турбаваць»"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Сачыць за спробамі разблакіроўкі экрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Сачыць за колькасцю няправільных набраных пароляў падчас разблакоўкі экрана і блакаваць планшэт або сціраць усе дадзеныя на ім, калі няправільны пароль набраны занадта шмат разоў."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Сачыць за колькасцю няправільна набраных пароляў падчас разблакіроўкі экрана і заблакіраваць прыладу Android TV або сцерці ўсе даныя на прыладзе, калі няправільны пароль набраны занадта шмат разоў."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Падчас разблакіроўкі экрана сачыць за колькасцю няправільна набраных пароляў і, калі няправільны пароль набраны занадта шмат разоў, заблакіраваць інфармацыйна-забаўляльную сістэму ці сцерці ў ёй усе даныя."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Сачыць за колькасцю няправільных набраных пароляў падчас разблакоўкі экрана і блакаваць тяэлефон або сціраць усе дадзеныя на ім, калі набрана занадта шмат няправільных пароляў."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Сачыць за колькасцю няправільна набраных пароляў падчас разблакіроўкі экрана і блакіраваць планшэт або сцерці ўсе даныя гэтага карыстальніка, калі няправільны пароль набраны занадта шмат разоў."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Сачыць за колькасцю няправільна набраных пароляў падчас разблакіроўкі экрана і заблакіраваць прыладу Android TV або сцерці ўсе даныя карыстальніка, калі няправільны пароль набраны занадта шмат разоў."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Падчас разблакіроўкі экрана сачыць за колькасцю няправільна набраных пароляў і, калі няправільны пароль набраны занадта шмат разоў, заблакіраваць інфармацыйна-забаўляльную сістэму ці сцерці ўсе даныя гэтага профілю."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Сачыць за колькасцю няправільна набраных пароляў падчас разблакіроўкі экрана і блакіраваць тэлефон або сцерці ўсе даныя гэтага карыстальніка, калі няправільны пароль набраны занадта шмат разоў."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Змяніць блакіроўку экрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Змяніць блакіроўку экрана."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Сцерці ўсе даныя"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Cцерці даныя з планшэта без папярэджання, выканаўшы скід да заводскіх даных."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Сцерці даныя з прылады Android TV без папярэджання, выканаўшы скід да заводскіх налад."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Падчас скіду да заводскіх налад сцерці даныя ў інфармацыйна-забаўляльнай сістэме без папярэджання."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Сцерці даныя з тэлефона без папярэджання, выканаўшы скід да заводскіх налад."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Сцерці карыстальніцкія даныя"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Сцерці даныя профілю"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Сцерці карыстальніцкія даныя"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Сцерці даныя гэтага карыстальніка на дадзеным планшэце без папярэджання."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Сцерці даныя карыстальніка з гэтай прылады Android TV без папярэджання."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Сцерці даныя гэтага профілю ў гэтай інфармацыйна-забаўляльнай сістэме без папярэджання."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Сцерці даныя гэтага карыстальніка на дадзеным тэлефоне без папярэджання."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Усталяваць глабальны проксі прылады"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Задаць агульны проксі-cервер прылады, які будзе выкарыстоўвацца, калі прылада актыўная. Толькі ўладальнік прылады можа задаць агульны проксі-сервер."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index cdd27d4..024c990 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Докоснете, за да изтриете модела на лицето си, след което добавете лицето си отново"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Настройване на отключването с лице"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Отключвайте телефона си, като го погледнете"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"За да използвате функцията „Отключване с лице“, включете "<b>"достъпа до камерата"</b>" от „Настройки > Поверителност“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройване на още начини за отключване на телефона"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Докоснете, за да добавите отпечатък"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Отключване с отпечатък"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Наблюдаване на опитите за отключване на екрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Наблюдава броя въведени неправилни пароли при отключването на екрана и заключва таблета или изтрива всички данни от него, ако неправилните пароли са твърде много."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Следене на броя на неправилно въведените пароли при отключване на екрана и заключване на устройството ви с Android TV или изтриване на всички данни от него, ако неуспешните опити са твърде много."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Наблюдава броя на неправилно въведените пароли при отключването на екрана и заключва основното устройство или изтрива всички данни от него, ако неуспешните опити са твърде много."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Наблюдава броя въведени неправилни пароли при отключването на екрана и заключва телефона или изтрива всички данни от него, ако неправилните пароли са твърде много."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Наблюдава броя на неправилно въведените пароли при отключване на екрана и заключва таблета или изтрива всички данни на този потребител, ако неуспешните опити са твърде много."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Следене на броя на неправилно въведените пароли при отключване на екрана и заключване на устройството ви с Android TV или изтриване на всички данни на този потребител, ако неуспешните опити са твърде много."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Наблюдава броя на неправилно въведените пароли при отключване на екрана и заключва основното устройство или изтрива всички данни в този потребителски профил, ако неуспешните опити са твърде много."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Наблюдава броя на неправилно въведените пароли при отключване на екрана и заключва телефона или изтрива всички данни на този потребител, ако неуспешните опити са твърде много."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Промяна на заключването на екрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Променя заключването на екрана."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Изтриване на всички данни"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Изтриване на данните в таблета без предупреждение чрез възстановяване на фабричните настройки."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Изтриване на данните от устройството ви с Android TV без предупреждение чрез възстановяване на фабричните настройки."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Изтрива данните на основното устройство без предупреждение чрез възстановяване на фабричните настройки."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Изтрива данните в телефона без предупреждение чрез възстановяване на фабричните настройки."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Изтриване на потребителските данни"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Изтриване на данните в потребителския профил"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Изтриване на потребителските данни"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Изтрива данните на този потребител от таблета без предупреждение."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Изтриване на данните на този потребител от устройството с Android TV без предупреждение."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Изтрива данните в този потребителски профил на основното устройство без предупреждение."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Изтрива данните на този потребител от телефона без предупреждение."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Задаване на глобален прокси сървър за устройството"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Задава глобалния прокси сървър за устройството, който да се използва, когато правилото е активирано. Само собственикът на устройството може да задава такъв сървър."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 057a409..76c4e3a 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপনার ফেস মডেল মুছে দেওয়ার জন্য ট্যাপ করুন এবং তারপরে আবার ফেস যোগ করুন"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"\'ফেস আনলক\' সেট আপ করুন"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপনার ফোনের দিকে তাকিয়ে এটিকে আনলক করুন"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"\'ফেস আনলক\' ফিচার ব্যবহার করতে \'সেটিংস ও গোপনীয়তা\' বিকল্পে গিয়ে "<b>"ক্যামেরায় অ্যাক্সেস দিন"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক করার জন্য বিভিন্ন উপায়ে সেট আপ করুন"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"একটি আঙ্গুলের ছাপ যোগ করতে ট্যাপ করুন"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ফিঙ্গারপ্রিন্ট আনলক"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্রিন আনলক করার প্রচেষ্টাগুলির উপরে নজর রাখুন"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"স্ক্রীণ আনলক করার সময় ভুলভাবে লেখা পাসওয়ার্ড প্রবেশের সংখ্যা মনিটার করে, এবং ট্যাবলেট লক করে এবং অনেক বার পাসওয়ার্ড ভুল ভাবে লেখা হলে ট্যাবলেটের ডেটা মুছে ফেলে৷"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"স্ক্রিন আনলক করার সময় কতবার ভুল পাসওয়ার্ড লেখা হচ্ছে তা মনিটর করুন এবং Android TV ডিভাইস লক করুন অথবা অনেকবার ভুল পাসওয়ার্ড লেখা হলে ডিভাইসের সব ডেটা মুছে ফেলুন।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ফোনের স্ক্রিন আনলক করার সময় কতবার ভুল পাসওয়ার্ড টাইপ করা হয়েছে তা মনিটর করুন। একাধিকবার ভুল পাসওয়ার্ড টাইপ করা হলে ইনফোটেইনমেন্ট সিস্টেম লক করুন অথবা এর সব ডেটা মুছে ফেলুন।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"স্ক্রীণ আনলক করার সময় ভুলভাবে লেখা পাসওয়ার্ড প্রবেশের সংখ্যা মনিটার করে, এবং ফোন লক করে এবং অনেক বার পাসওয়ার্ড ভুল ভাবে লেখা হলে ফোনের ডেটা মুছে ফেলে৷"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"স্ক্রিন আনলক করার সময় ভুলভাবে লেখা পাসওয়ার্ড প্রবেশের সংখ্যা মনিটার করে, এবং ট্যাবলেট লক করে এবং অনেক বার পাসওয়ার্ড ভুল ভাবে লেখা হলে ব্যবহারকারীর ডেটা মুছে ফেলে৷"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"স্ক্রিন আনলক করার সময় কতবার ভুল পাসওয়ার্ড লেখা হচ্ছে তা মনিটর করুন এবং Android TV ডিভাইস লক করুন অথবা অনেকবার ভুল পাসওয়ার্ড লেখা হলে সব ব্যবহারকারীর ডেটা মুছে ফেলুন।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ফোনের স্ক্রিন আনলক করার সময় কতবার ভুল পাসওয়ার্ড টাইপ করা হয়েছে তা মনিটর করুন। একাধিকবার ভুল পাসওয়ার্ড টাইপ করা হলে ইনফোটেইনমেন্ট সিস্টেম লক করুন অথবা এই প্রোফাইলের সব ডেটা মুছে ফেলুন।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"স্ক্রিন আনলক করার সময় ভুলভাবে লেখা পাসওয়ার্ড প্রবেশের সংখ্যা মনিটার করে, এবং ফোন লক করে এবং অনেক বার পাসওয়ার্ড ভুল ভাবে লেখা হলে ব্যবহারকারীর ডেটা মুছে ফেলে৷"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"স্ক্রিন লক পরিবর্তন করে"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"স্ক্রিন লক পরিবর্তন করুন৷"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"সমস্ত ডেটা মুছে দেয়"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ফ্যাক্টরি ডেটা আবার সেট কার্য সম্পাদনার দ্বারা কোনো রকম সতর্কতা ছাড়াই ট্যাবলেটের ডেটা মোছে৷"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ফ্যাক্টরি ডেটা রিসেট করে কোনও সতর্কতা ছাড়াই আপনার Android TV ডিভাইসের ডেটা মুছে ফেলুন।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ফ্যাক্টরি ডেটা রিসেট করে কোনও সতর্কতা মূলক বিজ্ঞপ্তি ছাড়াই আপনার ইনফোটেইনমেন্ট সিস্টেমের ডেটা মুছে ফেলুন।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ফ্যাক্টরি ডেটা রিসেট করে কোনও সতর্কতা ছাড়াই ফোনের ডেটা মোছে৷"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ব্যবহারকারীর ডেটা মুছুন"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"প্রোফাইল ডেটা মুছে ফেলুন"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ব্যবহারকারীর ডেটা মুছুন"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"সতর্কীকরণ ছাড়াই এই ট্যাবলেটে থাকা ব্যাবহারকার্রী ডেটা মুছে ফেলে৷"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"কোনও সতর্কতা ছাড়াই এই ব্যবহারকারীর ডেটা Android TV ডিভাইসটি থেকে মুছুন।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"কোনও সতর্কতা মূলক বিজ্ঞপ্তি ছাড়াই ইনফোটেইনমেন্ট সিস্টেমে থাকা এই প্রোফাইলের ডেটা মুছে ফেলুন।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"সতর্কীকরণ ছাড়াই এই ফোনে থাকা ব্যাবহারকার্রী ডেটা মুছে ফেলে৷"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ডিভাইসের বৈশ্বিক প্রক্সী সেট করে"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"নীতিযখন নীতি সক্ষম করা হয় তখন ডিভাইসের বৈশ্বিক প্রক্সী ব্যবহার করা হবে সেই হিসেবে সেট করে৷ শুধুমাত্র ডিভাইসের মালিক বৈশ্বিক প্রক্সী সেট করতে পারেন৷"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 3fef88b..1e3ec56 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -329,19 +329,19 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"poziva i upravlja pozivima"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Tjelesni senzori"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"pristupa podacima senzora o vašim vitalnim funkcijama"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Preuzima sadržaj prozora"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"preuzima sadržaj prozora"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Pregleda sadržaj prozora koji trenutno koristite."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Uključi opciju Istraživanje dodirom"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"uključi opciju Istraživanje dodirom"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Stavke koje dodirnete bit će izgovorene naglas, a ekran možete istraživati koristeći pokrete."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Prati tekst koji unosite"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"prati tekst koji unosite"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Obuhvata lične podatke kao što su brojevi kreditnih kartica i lozinke."</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Kontrolira uvećavanje prikaza na ekranu"</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"kontrolira uvećavanje prikaza na ekranu"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Kontrolira stepen uvećanja prikaza na ekranu i podešavanje položaja."</string>
- <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Praviti pokrete"</string>
+ <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"izvodi pokrete"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Može dodirivati, prevlačiti, hvatati prstima i praviti druge pokrete."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Pokreti otiska prsta"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"prepoznaje pokrete za otisak prsta"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Moguće je zabilježiti pokrete na senzoru za otisak prsta uređaja."</string>
- <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Snimanje ekrana"</string>
+ <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"pravi snimke ekrana"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Moguće je snimiti ekran."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"onemogućavanje ili mijenjanje statusne trake"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Dozvoljava aplikaciji onemogućavanje statusne trake ili dodavanje i uklanjanje sistemskih ikona."</string>
@@ -619,6 +619,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da izbrišete model lica, a zatim ponovo dodajte lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da koristite otključavanje licem, uključite "<b>"Pristup kameri"</b>" u meniju Postavke > Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da dodate otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -732,9 +733,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Prati pokušaje otključavanja ekrana"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Prati broj pogrešno unijetih lozinki prilikom otključavanja ekrana i zaključava tablet ili briše sve podatke s njega ukoliko se previše puta unese pogrešna lozinka."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Praćenje broja unosa netačnih lozinki pri otključavanju ekrana i zaključavanje Android TV uređaja ili brisanje svih podataka Android TV uređaja u slučaju prevelikog broja unosa netačnih lozinki."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati koliko puta je lozinka neispravno unijeta prilikom otključavanja ekrana i zaključava informativno-zabavni sistem ili briše sve podatke informativno-zabavnog sistema ako se lozinka neispravno unese previše puta."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Prati broj pogrešno unesenih lozinki prilikom otključavanja ekrana i zaključava telefon ili briše sve podatke s telefona ukoliko se previše puta unese pogrešna lozinka."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Prati broj neispravnih lozinki koje su unijete za otključavanje ekrana te zaključava tablet ili briše sve podatke ovog korisnika ukoliko je unijeto previše neispravnih lozinki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Praćenje broja unosa netačnih lozinki za otključavanje ekrana te zaključavanje Android TV uređaja ili brisanje svih podataka ovog korisnika u slučaju prekomjernog unosa netačnih lozinki."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Prati koliko puta je lozinka neispravno unijeta prilikom otključavanja ekrana i zaključava informativno-zabavni sistem ili briše sve podatke ovog profila ako se lozinka neispravno unese previše puta."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Prati broj neispravnih lozinki koje su unijete za otključavanje ekrana te zaključava telefon ili briše sve podatke ovog korisnika ukoliko je unijeto previše neispravnih lozinki."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Promjena zaključavanja ekrana"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Mijenja zaključavanje ekrana."</string>
@@ -743,10 +746,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje svih podataka"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Briše podatke s tableta bez upozorenja tako što ga vraća na fabričke postavke."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Brisanje podataka Android TV uređaja bez upozorenja vraćanjem uređaja na fabričke postavke."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Bez upozorenja briše podatke informativno-zabavnog sistema vraćanjem na fabričke postavke."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Briše podatke s telefona bez upozorenja vraćanjem telefona na fabričke postavke."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Izbriši podatke korisnika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Briše podatke profila"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Izbriši podatke korisnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Bez upozorenja briše podatke ovog korisnika sa ovog tableta."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Brisanje podataka ovog korisnika na Android TV uređaju bez upozorenja."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bez upozorenja briše podatke ovog profila na ovom informativno-zabavnom sistemu."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Bez upozorenja briše podatke ovog korisnika sa ovog telefona."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Postavlja globalni proksi uređaja"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Postavlja globalni proksi uređaja koji će se koristiti dok su smjernice omogućene. Samo vlasnik uređaja može postaviti globalni proksi."</string>
@@ -1899,7 +1905,7 @@
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je vaš administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Uredu"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije te neke mrežne veze."</string>
- <string name="battery_saver_description" msgid="8518809702138617167">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije te neke mrežne veze."</string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije te neke mrežne veze."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Radi smanjenja prijenosa podataka, Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali će to činiti rjeđe. Naprimjer, to može značiti da se slike ne prikazuju dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Uštedu podataka?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
@@ -2035,7 +2041,7 @@
<string name="demo_restarting_message" msgid="1160053183701746766">"Vraćanje uređaja na početne postavke…"</string>
<string name="suspended_widget_accessibility" msgid="6331451091851326101">"Onemogućen <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="conference_call" msgid="5731633152336490471">"Konferencijski poziv"</string>
- <string name="tooltip_popup_title" msgid="7863719020269945722">"Savjet za alat"</string>
+ <string name="tooltip_popup_title" msgid="7863719020269945722">"Skočni opis"</string>
<string name="app_category_game" msgid="4534216074910244790">"Igre"</string>
<string name="app_category_audio" msgid="8296029904794676222">"Muzika i zvuk"</string>
<string name="app_category_video" msgid="2590183854839565814">"Filmovi i videozapisi"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 7bc5905..594a33c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca per suprimir el teu model facial i, a continuació, torna a afegir la teva cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueig facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mira el telèfon per desbloquejar-lo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Per utilitzar Desbloqueig facial, activa "<b>"Accés a la càmera"</b>" a Configuració > Privadesa"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura més maneres de desbloquejar el dispositiu"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca per afegir una empremta digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueig amb empremta digital"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar els intents de desbloqueig de la pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja la tauleta o n\'esborra totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han introduït en intentar desbloquejar la pantalla i bloqueja el dispositiu Android TV o esborra totes les dades del dispositiu si s\'introdueixen massa contrasenyes incorrectes."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Supervisa el nombre de contrasenyes incorrectes introduïdes en desbloquejar la pantalla, i bloqueja el sistema d\'informació i entreteniment o n\'esborra totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisa el nombre de contrasenyes incorrectes introduïdes en desbloquejar la pantalla, i bloqueja el telèfon o esborra totes les dades del telèfon si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han escrit en intentar desbloquejar la pantalla i bloqueja la tauleta o n\'esborra totes les dades de l\'usuari si s\'escriuen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han introduït en intentar desbloquejar la pantalla i bloqueja el dispositiu Android TV o n\'esborra totes les dades de l\'usuari si s\'introdueixen massa contrasenyes incorrectes."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Supervisa el nombre de contrasenyes incorrectes introduïdes en desbloquejar la pantalla, i bloqueja el sistema d\'informació i entreteniment o esborra totes les dades d\'aquest perfil si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han escrit en intentar desbloquejar la pantalla i bloqueja el telèfon o n\'esborra totes les dades de l\'usuari si s\'escriuen massa contrasenyes incorrectes."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Canviar el bloqueig de pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Canvia el bloqueig de pantalla."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Esborrar totes les dades"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Esborra les dades de la tauleta sense avisar, i restableix les dades de fàbrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Suprimeix les dades del dispositiu Android TV sense previ avís mitjançant el restabliment de les dades de fàbrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Esborra les dades del sistema d\'informació i entreteniment sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Esborra les dades del telèfon sense avisar, i restableix les dades de fàbrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Esborrar les dades de l\'usuari"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Esborra les dades del perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Esborrar les dades de l\'usuari"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Esborra les dades de l\'usuari desades a la tauleta sense avisar-ne."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Esborra les dades de l\'usuari desades al dispositiu Android TV sense previ avís."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Esborra les dades del perfil d\'aquest sistema d\'informació i entreteniment sense avisar."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Esborra les dades de l\'usuari desades al telèfon sense avisar-ne."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir el servidor intermediari global del dispositiu"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Si la política s\'activa, s\'utilitza el servidor intermediari global del dispositiu. Només el propietari del dispositiu el pot establir."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 2c8477c..626e520f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Klepnutím svůj model obličeje smažte a potom ho přidejte znovu"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odemknutí obličejem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefon odemknete pohledem"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pokud chcete používat odemknutí obličejem, v Nastavení > Soukromí zapnetě "<b>"přístup k fotoaparátu"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte si více způsobů odemykání"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Klepnutím přidáte otisk prstu"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odemknutí otiskem prstu"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovat pokusy o odemknutí obrazovky"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout tablet nebo vymazat z tabletu všechna data, pokud bylo zadáno příliš mnoho nesprávných hesel."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud jich bude zadáno příliš mnoho, uzamknout zařízení Android TV nebo z něj vymazat všechna data."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout informační a zábavní systém nebo vymazat veškerá data v informačním a zábavním systému, pokud je zadáno příliš mnoho nesprávných hesel."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout telefon nebo vymazat z telefonu všechna data, pokud bylo zadáno příliš mnoho nesprávných hesel."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout tablet nebo vymazat veškerá data uživatele."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud jich bude zadáno příliš mnoho, uzamknout zařízení Android TV nebo z něj vymazat všechna data tohoto uživatele."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout informační a zábavní systém nebo vymazat veškerá data profilu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout telefon nebo vymazat veškerá data uživatele."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Změnit zámek obrazovky"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Změní se zámek obrazovky."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Vymazat všechna data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Bez upozornění smazat všechna data tabletu obnovením továrních dat."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Provést obnovení továrních dat a bez upozornění tím vymazat data v zařízení Android TV."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Bez upozornění se smažou všechna data informačního a zábavního systému obnovením továrních dat."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Bez upozornění se smažou všechna data telefonu obnovením továrních dat."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Vymazat data uživatele"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Vymazání profilových dat"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Vymazat data uživatele"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Vymazat data tohoto uživatele v tomto tabletu bez upozornění."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Bez upozornění vymazat data tohoto uživatele v tomto zařízení Android TV."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bez upozornění se smažou data tohoto profilu v informačním a zábavním systému."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Vymazat data tohoto uživatele v tomto telefonu bez upozornění."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Nastavit globální proxy server zařízení"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Nastaví globální proxy server, který bude používán, když je zásada zapnuta. Globální proxy server může nastavit pouze vlastník zařízení."</string>
@@ -992,7 +998,7 @@
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nOpravdu tuto stránku chcete opustit?"</string>
<string name="save_password_label" msgid="9161712335355510035">"Potvrdit"</string>
<string name="double_tap_toast" msgid="7065519579174882778">"Tip: Dvojitým klepnutím můžete zobrazení přiblížit nebo oddálit."</string>
- <string name="autofill_this_form" msgid="3187132440451621492">"Aut.vyp."</string>
+ <string name="autofill_this_form" msgid="3187132440451621492">"Autofill"</string>
<string name="setup_autofill" msgid="5431369130866618567">"Nastav aut. vyp."</string>
<string name="autofill_window_title" msgid="4379134104008111961">"Automatické vyplňování pomocí služby <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
<string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 353358d..7837c6e 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tryk for at slette din ansigtsmodel, og tilføj derefter dit ansigt igen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansigtslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås din telefon op ved at kigge på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Hvis du vil bruge ansigtslåsen, skal du aktivere "<b>"Kameraadgang"</b>" under Indstillinger > Privatliv"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måder at låse op på"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryk for at tilføje et fingeraftryk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Oplåsning med fingeraftryk"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåg forsøg på oplåsning af skærm"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Overvåg antallet af forkert indtastede adgangskoder, når du låser skærmen op, og lås din tablet, eller slet alle data i den, hvis der er indtastet for mange forkerte adgangskoder."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Registrer antallet af forkerte adgangskoder, der angives ved oplåsning af skærmen, og lås din Android TV-enhed, eller ryd alle dataene på din Android TV-enhed, hvis adgangskoden angives forkert for mange gange."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Registrer antallet af forkert indtastede adgangskoder, når du låser skærmen op, og lås infotainmentsystemet, eller slet alle data i infotainmentsystemet, hvis der er indtastet for mange forkerte adgangskoder."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Overvåg antallet af forkerte adgangskoder ved oplåsning af skærmen, og lås telefonen eller slet alle data på telefonen, hvis der er indtastet for mange forkerte adgangskoder."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Registrer antallet af forkerte adgangskoder, der angives ved oplåsning af skærmen, og lås din tablet, eller slet alle brugerens data, hvis adgangskoden tastes forkert for mange gange."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Registrer antallet af forkerte adgangskoder, der angives ved oplåsning af skærmen, og lås din Android TV-enhed, eller ryd alle brugerens data, hvis adgangskoden angives forkert for mange gange."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Registrer antallet af forkert indtastede adgangskoder, når du låser skærmen op, og lås infotainmentsystemet, eller slet alle data på denne profil, hvis der er indtastet for mange forkerte adgangskoder."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Registrer antallet af forkerte adgangskoder, der angives ved oplåsning af skærmen, og lås telefonen, eller slet alle brugerens data, hvis adgangskoden tastes forkert for mange gange."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Skifte skærmlås"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Skifter skærmlåsen."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Slette alle data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Slet din tablets data uden varsel ved at gendanne fabriksindstillingerne."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ryd dataene på din Android TV-enhed uden at gendanne fabriksdataene."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ryd infotainmentsystemets data uden varsel ved at gendanne fabriksindstillingerne."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Sletter telefonens data uden varsel ved at gendanne fabriksindstillingerne."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Slet brugerdata"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Ryd profildata"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Slet brugerdata"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Slet denne brugers data på denne tablet uden varsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ryd denne brugers data på denne Android TV-enhed uden varsel."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ryd denne profils data i dette infotainmentsystem uden varsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Slet denne brugers data på denne telefon uden varsel."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Angiv enhedens globale proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Indstil den globale proxy for enheden, der skal bruges, mens politikken er aktiveret. Det er kun enhedens ejer, der kan indstille den globale proxy."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index a974e90..36ed9b2 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tippe, um dein Gesichtsmodell zu löschen, und füge es dann noch einmal hinzu"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Entsperrung per Gesichtserkennung einrichten"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Entsperre dein Smartphone, indem du es ansiehst"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Wenn du die Entsperrung per Gesichtserkennung verwenden möchtest, aktiviere in den Einstellungen unter „Datenschutz“ die Option "<b>"Kamerazugriff"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weitere Möglichkeiten zum Entsperren einrichten"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tippe, um einen Fingerabdruck hinzuzufügen"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Entsperrung per Fingerabdruck"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Versuche zum Entsperren des Displays überwachen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays überwachen und Tablet sperren oder alle Daten auf dem Tablet löschen, wenn zu häufig ein falsches Passwort eingegeben wird."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Es wird überwacht, wie oft beim Versuch, den Bildschirm zu entsperren, ein falsches Passwort eingegeben wird. Wenn es zu viele Fehlversuche gibt, wird das Android TV-Gerät gesperrt oder alle Daten darauf werden gelöscht."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays erfassen und Infotainmentsystem sperren oder alle Daten des Infotainmentsystems löschen, wenn zu häufig ein falsches Passwort eingegeben wird."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Bildschirms überwachen und Telefon sperren oder alle Daten auf dem Telefon löschen, wenn zu häufig ein falsches Passwort eingegeben wird."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays überwachen und Tablet sperren oder alle Daten dieses Nutzers löschen, wenn zu häufig ein falsches Passwort eingegeben wird"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Es wird überwacht, wie oft beim Versuch, den Bildschirm zu entsperren, ein falsches Passwort eingegeben wird. Wenn es zu viele Fehlversuche gibt, wird das Android TV-Gerät gesperrt oder alle Daten dieses Nutzers werden gelöscht."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays erfassen und Infotainmentsystem sperren oder alle Daten dieses Profils löschen, wenn zu häufig ein falsches Passwort eingegeben wird."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays überwachen und Smartphone sperren oder alle Daten dieses Nutzers löschen, wenn zu häufig ein falsches Passwort eingegeben wird"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Displaysperre ändern"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ändern der Displaysperre"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Alle Daten löschen"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Auf Werkseinstellungen zurücksetzen und Daten auf dem Tablet ohne Warnung löschen"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Du kannst die Daten auf deinem Android TV-Gerät ohne vorherige Warnung löschen, indem du es auf die Werkseinstellungen zurücksetzt."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Gerät auf Werkseinstellungen zurücksetzen und damit Daten des Infotainmentsystems ohne Warnung löschen."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Gerät auf Werkseinstellungen zurücksetzen und damit Daten auf dem Telefon ohne Warnung löschen"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Nutzerdaten löschen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profildaten löschen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Nutzerdaten löschen"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Daten dieses Nutzers auf diesem Tablet ohne vorherige Warnung löschen"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Daten dieses Nutzers auf diesem Android TV-Gerät werden ohne vorherige Warnung gelöscht."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Daten dieses Profils in diesem Infotainmentsystem ohne Warnung löschen."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Daten dieses Nutzers auf diesem Smartphone ohne vorherige Warnung löschen"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Den globalen Proxy des Geräts festlegen"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Bei aktivierter Richtlinie zu verwendenden globalen Geräteproxy festlegen. Nur der Eigentümer des Geräts kann den globalen Proxy festlegen."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 03d978e..b8b0e0b 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Πατήστε για να διαγράψετε το μοντέλο προσώπου και, στη συνέχεια, προσθέστε το πρόσωπό σας ξανά."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ρύθμιση της λειτουργίας Ξεκλείδωμα με το πρόσωπο"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ξεκλειδώστε το τηλέφωνό σας απλώς κοιτώντας το"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Για να χρησιμοποιήσετε τη λειτουργία Ξεκλείδωμα με το πρόσωπο, ενεργοποιήστε την επιλογή "<b>"Πρόσβαση στην κάμερα"</b>" από το μενού Ρυθμίσεις > Απόρρητο"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ρυθμίστε περισσότερους τρόπους ξεκλειδώματος"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Πατήστε για να προσθέσετε δακτυλικό αποτύπωμα"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ξεκλείδωμα με δακτυλικό αποτύπωμα"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Παρακολούθηση του αριθμού λανθασμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλείδωμα του tablet ή διαγραφή όλων των δεδομένων του σε περίπτωση πληκτρολόγησης πάρα πολλών εσφαλμένων κωδικών πρόσβασης."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Παρακολουθήστε τον αριθμό των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλειδώστε τη συσκευή Android TV ή διαγράψτε όλα τα δεδομένα της σε περίπτωση εισαγωγής εσφαλμένων κωδικών πρόσβασης πάρα πολλές φορές."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Παρακολούθηση του αριθμού των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλείδωμα του συστήματος ενημέρωσης και ψυχαγωγίας ή διαγραφή όλων των δεδομένων του εάν πληκτρολογηθούν πολλοί εσφαλμένοι κωδικοί πρόσβασης."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Παρακολούθηση του αριθμού λανθασμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλείδωμα του τηλεφώνου ή διαγραφή όλων των δεδομένων του σε περίπτωση πληκτρολόγησης πάρα πολλών εσφαλμένων κωδικών πρόσβασης."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Παρακολουθήστε τον αριθμό των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλειδώστε το tablet ή διαγράψτε όλα τα δεδομένα του χρήστη, σε περίπτωση εισαγωγής πάρα πολλών εσφαλμένων κωδικών πρόσβασης."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Παρακολουθήστε τον αριθμό των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλειδώστε τη συσκευή Android TV ή διαγράψτε όλα τα δεδομένα χρήστη σε περίπτωση εισαγωγής εσφαλμένων κωδικών πρόσβασης πάρα πολλές φορές."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Παρακολούθηση του αριθμού των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλείδωμα του συστήματος ενημέρωσης και ψυχαγωγίας ή διαγραφή όλων των δεδομένων αυτού του προφίλ εάν πληκτρολογηθούν πολλοί εσφαλμένοι κωδικοί πρόσβασης."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Παρακολουθήστε τον αριθμό των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλειδώστε το τηλέφωνο ή διαγράψτε όλα τα δεδομένα του χρήστη, σε περίπτωση εισαγωγής πάρα πολλών εσφαλμένων κωδικών πρόσβασης."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Αλλαγή του κλειδώματος οθόνης"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Αλλαγή του κλειδώματος οθόνης."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Διαγραφή όλων των δεδομένων"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Διαγραφή των δεδομένων του tablet χωρίς προειδοποίηση με επαναφορά των εργοστασιακών ρυθμίσεων."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Διαγράψτε τα δεδομένα της συσκευής σας Android TV χωρίς προειδοποίηση εκτελώντας επαναφορά των εργοστασιακών δεδομένων."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Διαγραφή των δεδομένων του συστήματος ενημέρωσης και ψυχαγωγίας χωρίς προειδοποίηση με εκτέλεση επαναφοράς εργοστασιακών ρυθμίσεων."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Διαγραφή των δεδομένων του τηλεφώνου χωρίς προειδοποίηση με επαναφορά των εργοστασιακών ρυθμίσεων."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Διαγραφή δεδομένων χρήστη"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Διαγραφή δεδομένων προφίλ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Διαγραφή δεδομένων χρήστη"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Διαγραφή των δεδομένων αυτού του χρήστη σε αυτό το tablet χωρίς προειδοποίηση."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Διαγράψτε τα δεδομένα χρήστη σε αυτή τη συσκευή Android TV χωρίς προειδοποίηση."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Διαγραφή των δεδομένων αυτού του προφίλ σε αυτό το σύστημα ενημέρωσης και ψυχαγωγίας χωρίς προειδοποίηση."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Διαγραφή των δεδομένων αυτού του χρήστη σε αυτό το τηλέφωνο χωρίς προειδοποίηση."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Ρύθμιση του γενικού διακομιστή μεσολάβησης της συσκευής"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Ρυθμίστε τη χρήση του γενικού διακομιστή μεσολάβησης της συσκευής, ενώ η πολιτική είναι ενεργοποιημένη. Μόνο ο κάτοχος της συσκευής μπορεί να ρυθμίσει τον γενικό διακομιστής μεσολάβησης."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 04158b4..6119131 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Delete all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Delete your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Delete this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 3b019f4..0373599 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Erase all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Delete your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Delete this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 91d39a9..abcc85d 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Delete all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Delete your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Delete this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index fe23e63..79cb53c 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Delete all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Delete your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Delete this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 787de9b..d5d9715 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or erase all your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed. when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed. when unlocking the screen, and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or erase all this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Erase all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Erase your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Erase this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 2ec544d..e38541d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -325,19 +325,19 @@
<string name="permgrouplab_phone" msgid="570318944091926620">"Teléfono"</string>
<string name="permgroupdesc_phone" msgid="270048070781478204">"hacer y administrar llamadas telefónicas"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensores corporales"</string>
- <string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceder a los datos del sensor acerca de tus signos vitales"</string>
+ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceder a los datos de sensores acerca de tus signos vitales"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contenido de las ventanas"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contenido de la ventana con la que estés interactuando."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspeccionará el contenido de la ventana con la que estés interactuando."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar la Exploración táctil"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Los elementos que presiones se dirán en voz alta y podrás explorar la pantalla mediante gestos."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Observar el texto que escribes"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Incluye datos personales, como números de tarjeta de crédito y contraseñas."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Controlar la ampliación de pantalla"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlará el posicionamiento y el nivel de zoom de la pantalla."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Usar gestos"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Permite presionar, deslizar, pellizcar y usar otros gestos."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos del sensor de huellas dactilares"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Captura los gestos que se hacen en el sensor de huellas dactilares del dispositivo."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Capturará los gestos que se hacen en el sensor de huellas dactilares del dispositivo."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Tomar captura de pantalla"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede tomar una captura de la pantalla."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"desactivar o modificar la barra de estado"</string>
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Presiona para borrar el modelo de rostro y, luego, vuelve a agregar tu rostro"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar Desbloqueo facial, activa el "<b>"Acceso a la cámara"</b>" en Configuración y privacidad"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloquear el dispositivo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Presiona para agregar una huella dactilar"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huellas dactilares"</string>
@@ -722,16 +723,18 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de la función No interrumpir."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el propietario inicie el uso de permisos para una app. No debería requerirse para apps normales."</string>
- <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Acceder a los datos del sensor a una tasa de muestreo alta"</string>
- <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que la app tome una muestra de los datos del sensor a una tasa superior a 200 Hz"</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Acceder a los datos de sensores a una tasa de muestreo alta"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que la app tome una muestra de los datos de sensores a una tasa superior a 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer reglas de contraseña"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlar la longitud y los caracteres permitidos en las contraseñas y los PIN para el bloqueo de pantalla."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisa los intentos para desbloquear la pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Controla la cantidad de contraseñas incorrectas ingresadas al desbloquear la pantalla y bloquea la tablet o borra todos los datos de la tablet si se ingresaron demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Supervisa la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquea el dispositivo Android TV o borra todos sus datos si se ingresan demasiadas contraseñas incorrectas."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear el sistema de infoentretenimiento, o borrar todos los datos del sistema, si se ingresaron demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisar la cantidad de contraseñas ingresadas incorrectamente al desbloquear la pantalla, y bloquear el dispositivo o borrar todos sus datos si se ingresan demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear la tablet, o borrar todos los datos del usuario, si se ingresan demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Supervisa la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquea el dispositivo Android TV o borra todos los datos del usuario si se ingresan demasiadas contraseñas incorrectas."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear el sistema de infoentretenimiento, o borrar todos los datos de perfil, si se ingresaron demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear el teléfono, o borrar todos los datos del usuario, si se ingresan demasiadas contraseñas incorrectas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Cambiar el bloqueo de pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Cambia el bloqueo de pantalla."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Borrar todos los datos"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Eliminar los datos de la tablet sin avisar y restablecer la configuración de fábrica"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Restablece la configuración de fábrica para borrar los datos del dispositivo Android TV sin previo aviso."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Permite borrar los datos del sistema de infoentretenimiento sin previo aviso mediante el restablecimiento de la configuración de fábrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Borra los datos del dispositivo sin avisar y restablece la configuración de fábrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Borrar los datos del usuario"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Borrar los datos de perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Borrar los datos del usuario"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Permite borrar los datos del usuario en esta tablet sin previo aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Borra los datos del usuario en este dispositivo Android TV sin previo aviso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Permite borrar los datos de perfil en este sistema de infoentretenimiento sin previo aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Permite borrar los datos del usuario en este teléfono sin previo aviso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Configura el proxy global de dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Configura el proxy global de dispositivo que se usará mientras se habilita la política. Solo el propietario del dispositivo puede configurar el proxy global."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index afc0644..ebc7cb12 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca para eliminar tu modelo facial y luego añade de nuevo tu cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar Desbloqueo Facial, habilita el "<b>"acceso a la cámara"</b>" en Ajustes y privacidad"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloqueo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para añadir una huella digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huella digital"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar los intentos de desbloqueo de pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Controla el número de contraseñas incorrectas introducidas al desbloquear la pantalla y bloquea el tablet o elimina todos sus datos si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Comprueba cuántas veces se han introducido contraseñas incorrectas para desbloquear la pantalla y, si te parece que han sido demasiadas, bloquea tu dispositivo Android TV o borra todos sus datos."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Controla el número de contraseñas incorrectas introducidas al desbloquear la pantalla y bloquea el sistema de infoentretenimiento o borra todos sus datos si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Controla el número de contraseñas incorrectas introducidas al desbloquear la pantalla y bloquea el teléfono o elimina todos sus datos si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Controla el número de contraseñas incorrectas introducidas para desbloquear la pantalla y bloquea el tablet o borra todos los datos del usuario si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Comprueba cuántas veces se han introducido contraseñas incorrectas para desbloquear la pantalla y, si te parece que han sido demasiadas, bloquea tu dispositivo Android TV o borra todos los datos de este usuario."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Controla el número de contraseñas incorrectas introducidas al desbloquear la pantalla y bloquea el sistema de infoentretenimiento o borra todos los datos del perfil si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Controla el número de contraseñas incorrectas introducidas para desbloquear la pantalla y bloquea el teléfono o borra todos los datos del usuario si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Cambiar el bloqueo de pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Cambia el bloqueo de pantalla"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Borrar todos los datos"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Borrar los datos del tablet sin avisar restableciendo el estado de fábrica"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Restablece los datos de fábrica de tu dispositivo Android TV, eliminando sin previo aviso los datos que tuviera."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Borra los datos del sistema de infoentretenimiento sin avisar restableciendo el estado de fábrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Borra los datos del teléfono sin avisar restableciendo el estado de fábrica"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Borrar datos del usuario"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Borrar datos del perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Borrar datos del usuario"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Borra los datos del usuario en este tablet sin avisar."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Eliminar los datos de este usuario del dispositivo Android TV sin previo aviso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Borra los datos del perfil de este sistema de infoentretenimiento sin avisar."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Borra los datos del usuario en este teléfono sin avisar."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir el servidor proxy global"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Define el servidor proxy global que se debe utilizar mientras la política esté habilitada. Solo el propietario del dispositivo puede definir el proxy global."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 70de8d9..dcfdd58 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Puudutage näomudeli kustutamiseks, seejärel lisage oma nägu uuesti"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Näoga avamise seadistamine"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avage telefon seda vaadates"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Näoga avamise kasutamiseks lülitage menüüs Seaded > Privaatsus sisse "<b>"juurdepääs kaamerale"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Seadistage rohkem viise avamiseks"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Puudutage sõrmejälje lisamiseks"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sõrmejäljega avamine"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekraani avamiskatsete jälgimine"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Jälgib ekraani avamisel valesti sisestatud paroolide arvu ja lukustab tahvelarvuti või kustutab kõik selle andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Jälgitakse ekraanikuva avamisel sisestatud valede paroolide arvu ja lukustatakse Android TV seade või kustutatakse kõik Android TV seadme andmed, kui vale parool sisestatakse liiga palju kordi."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Jälgib ekraani avamisel valesti sisestatud paroolide arvu ning lukustab teabe ja meelelahutuse süsteemi või kustutab kõik selle andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Jälgib ekraani avamisel valesti sisestatud paroolide arvu ja lukustab telefoni või kustutab kõik selle andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Jälgitakse ekraani avamisel sisestatud valede paroolide arvu ja lukustatakse tahvelarvuti või kustutatakse kõik selle kasutaja andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Jälgitakse ekraanikuva avamisel sisestatud valede paroolide arvu ja lukustatakse Android TV seade või kustutatakse kõik selle kasutaja andmed, kui vale parool sisestatakse liiga palju kordi."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Jälgitakse ekraani avamisel sisestatud valede paroolide arvu ning lukustatakse teabe ja meelelahutuse süsteem või kustutatakse kõik selle profiili andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Jälgitakse ekraani avamisel sisestatud valede paroolide arvu ja lukustatakse telefon või kustutatakse kõik selle kasutaja andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ekraaniluku muutmine"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Muutke ekraanilukku."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Kõikide andmete kustutamine"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Kustutage tahvelarvuti andmed hoiatamata, lähtestades arvuti tehaseandmetele."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Kustutatakse teie Android TV seadme andmed ilma hoiatamata, lähtestades seadme tehase andmetele."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Teabe ja meelelahutuse süsteemi andmete hoiatamata kustutamine tehase andmetele lähtestamise abil."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Telefoniandmete hoiatuseta kustutamine, lähtestades telefoni tehaseseadetele."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Kasutaja andmete kustutamine"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profiili andmete kustutamine"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Kasutaja andmete kustutamine"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Kustutatakse selle kasutaja andmed sellest tahvelarvutist ilma hoiatamata."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Kustutatakse selle kasutaja andmed sellest Android TV seadmest ilma hoiatamata."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Selle profiili andmete hoiatamata kustutamine teabe ja meelelahutuse süsteemist."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Kustutatakse selle kasutaja andmed sellest telefonist ilma hoiatamata."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Määra seadme globaalne puhverserver"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Määratakse kasutatava seadme üldine puhverserver, kui reegel on lubatud. Üldise puhverserveri saab määrata ainult seadme omanik."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 72f98c6..d6b0e5a 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Sakatu hau aurpegi-eredua ezabatzeko eta, gero, gehitu aurpegia berriro"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguratu aurpegi bidez desblokeatzeko eginbidea"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonoa desblokeatzeko, begira iezaiozu"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aurpegi bidez desblokeatzeko aukera erabiltzeko, aktibatu "<b>"kamera atzitzeko baimena"</b>" Ezarpenak > Pribatutasuna atalean"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguratu telefonoa desblokeatzeko modu gehiago"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Sakatu hau hatz-marka bat gehitzeko"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Hatz-marka bidez desblokeatzea"</string>
@@ -670,10 +671,10 @@
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Kontu baten sinkronizazio-ezarpenak aldatzeko baimena ematen die aplikazioei. Adibidez, Jendea aplikazioa kontu batekin sinkronizatzeko erabil daiteke."</string>
<string name="permlab_readSyncStats" msgid="3747407238320105332">"irakurri sinkronizazio-estatistikak"</string>
<string name="permdesc_readSyncStats" msgid="3867809926567379434">"Kontu baten sinkronizazio-estatistikak irakurtzeko baimena ematen dio; besteak beste, sinkronizazio-gertaeren historia eta sinkronizatutako datu kopurua."</string>
- <string name="permlab_sdcardRead" msgid="5791467020950064920">"Irakurri biltegiratze partekatuko edukia"</string>
- <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Biltegiratze partekatuko edukia irakurtzeko baimena ematen die aplikazioei."</string>
- <string name="permlab_sdcardWrite" msgid="4863021819671416668">"aldatu edo ezabatu biltegiratze partekatuko edukia"</string>
- <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Biltegiratze partekatuko edukian idazteko baimena ematen die aplikazioei."</string>
+ <string name="permlab_sdcardRead" msgid="5791467020950064920">"Irakurri biltegi partekatuko edukia"</string>
+ <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Biltegi partekatuko edukia irakurtzeko baimena ematen die aplikazioei."</string>
+ <string name="permlab_sdcardWrite" msgid="4863021819671416668">"aldatu edo ezabatu biltegi partekatuko edukia"</string>
+ <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Biltegi partekatuko edukian idazteko baimena ematen die aplikazioei."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"egin/jaso SIP deiak"</string>
<string name="permdesc_use_sip" msgid="3590270893253204451">"SIP deiak egitea eta jasotzeko baimena ematen die aplikazioei."</string>
<string name="permlab_register_sim_subscription" msgid="1653054249287576161">"erregistratu telekomunikabideekiko SIM konexio berriak"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gainbegiratu pantaila desblokeatzeko saiakerak"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Kontrolatu pantaila desblokeatzen saiatzean idatzitako pasahitz oker kopurua, eta blokeatu tableta edo ezabatu bere datuak pasahitza gehiegitan oker idazten bada."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Kontrolatu zenbat aldiz idatzi duzun oker pasahitza pantaila desblokeatzen saiatzean, eta blokeatu Android TV gailua edo ezabatu bertako datu guztiak pasahitza gehiegitan idazten baduzu oker."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Kontrolatu zenbatetan idazten duzun pasahitza oker pantaila desblokeatzen saiatzean eta, gehiegitan idazten bada oker, blokeatu informazio- eta aisia-sistema edo ezabatu hango eduki guztia."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Kontrolatu pantaila desblokeatzen saiatzean idatzitako pasahitz oker kopurua, eta blokeatu telefonoa edo ezabatu bere datuak pasahitza gehiegitan oker idazten bada."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Kontrolatu pantaila desblokeatzen saiatzean idatzitako pasahitz oker kopurua, eta blokeatu tableta edo ezabatu erabiltzailearen datuak pasahitza gehiegitan oker idazten bada."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Kontrolatu zenbat aldiz idatzi duzun oker pasahitza pantaila desblokeatzen saiatzean, eta blokeatu Android TV gailua edo ezabatu erabiltzailearen datuak pasahitza gehiegitan idazten baduzu oker."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Kontrolatu zenbatetan idazten duzun pasahitza oker pantaila desblokeatzen saiatzean eta, gehiegitan idazten bada oker, blokeatu informazio- eta aisia-sistema edo ezabatu profil honetako eduki guztia."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Kontrolatu pantaila desblokeatzen saiatzean idatzitako pasahitz oker kopurua, eta blokeatu telefonoa edo ezabatu erabiltzailearen datuak pasahitza gehiegitan oker idazten bada."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Aldatu pantailaren blokeoa"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Aldatu pantailaren blokeoa."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Ezabatu datu guztiak"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ezabatu tabletaren datuak abisatu gabe, jatorrizko datuak berrezarrita."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ezabatu Android TV gailuaren datuak abisatu gabe eta berrezarri jatorrizko datuak."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Berrezarri informazio- eta aisia-sistemako jatorrizko datuak abisatu gabe, bertan zegoen eduki guztia ezabatzeko."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ezabatu telefonoaren datuak abisatu gabe, jatorrizko datuak berrezarrita."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Ezabatu erabiltzailearen datuak"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Ezabatu profileko eduki guztia"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Ezabatu erabiltzailearen datuak"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ezabatu erabiltzaileak tabletan dituen datuak abisatu gabe."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ezabatu erabiltzaileak Android TV gailuan dituen datuak abisatu gabe."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ezabatu informazio- eta aisia-sisteman dagoen profil honetako eduki guztia abisatu gabe."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ezabatu erabiltzaileak telefonoan dituen datuak abisatu gabe."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Ezarri gailuaren proxy orokorra"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Ezarri gailuaren proxy orokorra gidalerroak gaituta dauden bitartean erabiltzeko. Gailuaren jabeak soilik ezar dezake proxy orokorra."</string>
@@ -1521,7 +1527,7 @@
<item quantity="one">Emaitza bat</item>
</plurals>
<string name="action_mode_done" msgid="2536182504764803222">"Eginda"</string>
- <string name="progress_erasing" msgid="6891435992721028004">"Biltegiratze partekatuko eduki guztia ezabatzen…"</string>
+ <string name="progress_erasing" msgid="6891435992721028004">"Biltegi partekatuko eduki guztia ezabatzen…"</string>
<string name="share" msgid="4157615043345227321">"Partekatu"</string>
<string name="find" msgid="5015737188624767706">"Aurkitu"</string>
<string name="websearch" msgid="5624340204512793290">"Web-bilaketa"</string>
@@ -1576,7 +1582,7 @@
<string name="action_menu_overflow_description" msgid="4579536843510088170">"Aukera gehiago"</string>
<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">"Barneko memoria partekatua"</string>
+ <string name="storage_internal" msgid="8490227947584914460">"Barneko biltegi partekatua"</string>
<string name="storage_sd_card" msgid="3404740277075331881">"SD txartela"</string>
<string name="storage_sd_card_label" msgid="7526153141147470509">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD txartela"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"USB bidezko unitatea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 9f72044..c502b80 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"برای حذف مدل چهرهتان ضربه بزنید، سپس چهرهتان را دوباره اضافه کنید"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"راهاندازی «قفلگشایی با چهره»"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"برای باز کردن قفل تلفن خود به آن نگاه کنید"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"برای استفاده از «قفلگشایی با چهره»، "<b>"دسترسی به دوربین"</b>" را در «تنظیمات > حریمخصوصی» روشن کنید"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"راهاندازی روشهای بیشتر برای باز کردن قفل"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"برای افزودن اثر انگشت، ضربه بزنید"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"قفلگشایی با اثر انگشت"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"پایش تلاشهای باز کردن قفل صفحه"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"تعداد گذرواژههای نادرست تایپ شده را هنگام بازکردن قفل صفحه کنترل میکند، و اگر دفعات زیادی گذرواژه نادرست وارد شود رایانهٔ لوحی را قفل میکند و همه دادههای رایانهٔ لوحی را پاک میکند."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"بر تعداد گذرواژههای نادرستی که هنگام بازکردن قفل صفحه تایپ میشود، نظارت میکند، و اگر گذرواژههای نادرست دفعات زیادی تایپ شوند، Android TV را قفل میکند یا همه دادههای آن را پاک میکند."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"بر تعداد گذرواژههای نادرست تایپشده در زمان باز کردن قفل صفحه نظارت میشود و اگر گذرواژههای نادرست دفعات خیلی زیادی تایپ شود، سیستم اطلاعات-سرگرمی قفل میشود یا همه دادههای سیستم اطلاعات-سرگرمی پاک میشود."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"تعداد گذرواژههای نادرست تایپشده را هنگام بازکردن قفل صفحه کنترل میکند و اگر چندین بار گذرواژههای نادرست وارد شود، تلفن را قفل میکند یا همه دادههای تلفن را پاک میکند."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده، نظارت میکند، و اگر تعداد گذرواژههای تایپ شده نادرست بیش از حد بود، رایانه لوحی را قفل میکند یا کلیه دادههای کاربر را پاک میکند."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ میشود، نظارت میکند، و اگر گذرواژههای نادرست دفعات زیادی تایپ شوند، دستگاه Android TV را قفل یا همه دادههای کاربر را پاک میکند."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده نظارت میشود و اگر گذرواژههای نادرست دفعات خیلی زیادی تایپ شود، تلفن قفل میشود یا همه دادههای کاربر پاک میشود."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده، نظارت میکند، و اگر تعداد گذرواژههای تایپ شده نادرست بیش از حد بود، تلفن را قفل میکند یا کلیه دادههای کاربر را پاک میکند."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"تغییر قفل صفحه"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"قفل صفحه را تغییر میدهد."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"پاک کردن تمام دادهها"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"با انجام بازنشانی دادههای کارخانه، دادههای رایانهٔ لوحی بدون هشدار پاک میشود."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"دادههای دستگاه Android TV شما، بدون نشان داده شدن هشدار و با انجام بازنشانی دادههای کارخانه، پاک میشود."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"دادههای سیستم اطلاعات-سرگرمی بدون هشدار و با انجام بازنشانی دادههای کارخانه پاک میشود."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"با انجام بازنشانی دادههای کارخانه، دادههای تلفن بدون هشدار پاک میشود."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"پاک کردن دادههای کاربر"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"پاک کردن دادههای نمایه"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"پاک کردن دادههای کاربر"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"دادههای این کاربر را در این رایانه لوحی بدون هشدار پاک میکند."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"بدون نشان داده شدن هشدار، دادههای این کاربر در این دستگاه Android TV پاک میشود."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"دادههای این نمایه در این سیستم اطلاعات-سرگرمی بدوم هشدار پاک میشود."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"دادههای این کاربر را در این تلفن بدون هشدار پاک میکند."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"تنظیم پروکسی جهانی دستگاه"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"پروکسی کلی دستگاه را برای استفاده هنگام فعال بودن این خطمشی تنظیم میکند. تنها مالک دستگاه میتواند پروکسی کلی را تنظیم کند."</string>
@@ -909,7 +915,7 @@
<string name="emergency_calls_only" msgid="3057351206678279851">"فقط تماسهای اضطراری"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"شبکه قفل شد"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"سیم کارت با PUK قفل شده است."</string>
- <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"لطفاً به راهنمای کاربر مراجعه کرده یا با مرکز پشتیبانی از مشتریان تماس بگیرید."</string>
+ <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"لطفاً به «راهنمای کاربر» مراجعه کنید یا با مرکز «مراقبت از مشتریان» تماس بگیرید."</string>
<string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"سیم کارت قفل شد."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"بازگشایی قفل سیم کارت…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. \n\nپساز <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 388a586b..26cfa93 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Poista kasvomalli napauttamalla ja lisää sitten kasvosi uudelleen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ota kasvojentunnistusavaus käyttöön"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avaa puhelimesi lukitus katsomalla laitetta"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Jos haluat käyttää kasvojentunnistusavausta, valitse Asetukset > Yksityisyys ja laita "<b>"Pääsy kameraan"</b>" päälle"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ota käyttöön lisää tapoja avata lukitus"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Napauta lisätäksesi sormenjälki"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sormenjälkiavaus"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Tarkkailla näytön avaamisyrityksiä"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Valvoo väärien salasanojen lukumäärää näytön lukituksen poistossa sekä lukitsee tablet-laitteen tai poistaa sen tiedot, jos salasana syötetään väärin liian monta kertaa."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Valvo väärien salasanojen määrää ruudun lukitusta avattaessa ja lukitse Android TV ‑laite tai poista kaikki Android TV ‑laitteen tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Valvoo väärien salasanojen lukumäärää näytön lukituksen poistossa ja lukitsee infotainment-järjestelmän tai poistaa sen kaiken datan, jos väärä salasana syötetään liian monta kertaa."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Valvoo väärien salasanojen lukumäärää näytön lukituksen poistossa ja lukitsee puhelimen tai poistaa sen kaikki tiedot, jos väärä salasana syötetään liian monta kertaa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Valvo väärien salasanojen määrää ruudun lukitusta avattaessa ja lukitse tabletti tai poista kaikki tämän käyttäjän tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Valvo väärien salasanojen määrää ruudun lukitusta avattaessa ja lukitse Android TV ‑laite tai poista kaikki tämän käyttäjän tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Valvoo väärien salasanojen määrää näytön lukituksen poistossa ja lukitsee infotainment-järjestelmän tai poistaa kaiken tämän profiilin datan, jos väärä salasana syötetään liian monta kertaa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Valvo väärien salasanojen määrää ruudun lukitusta avattaessa ja lukitse puhelin tai poista kaikki tämän käyttäjän tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Muuttaa näytön lukituksen"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Muuttaa näytön lukituksen."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Pyyhkiä kaikki tiedot"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Tyhjennä tablet-laitteen tiedot varoituksetta palauttamalla tehdasasetukset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Tyhjentää Android TV ‑laitteen tiedot ilman varoitusta palauttamalla tehdasasetukset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Poistaa infotainment-järjestelmän datan ilman varoitusta palauttamalla tehdasasetukset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Tyhjentää puhelimen tiedot varoituksetta palauttamalla tehdasasetukset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Pyyhi käyttäjän tiedot"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profiilidatan poistaminen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Pyyhi käyttäjän tiedot"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Pyyhi tämän käyttäjän tiedot tabletista ilman varoitusta."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Tyhjentää tämän käyttäjän tiedot Android TV ‑laitteesta ilman varoitusta."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Poistaa tämän profiilin datan tästä infotainment-järjestelmästä ilman varoitusta."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Pyyhi tämän käyttäjän tiedot puhelimesta ilman varoitusta."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Aseta laitteen yleinen välityspalvelin"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Aseta laitteen yleinen välityspalvelin käyttöön, kun käytäntö on käytössä. Vain laitteen omistaja voi asettaa yleisen välityspalvelimen."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 4b0f4bb..c3f1bc3 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -551,10 +551,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Autorise l\'application à apprendre le niveau de complexité de l\'écran de verrouillage (élevé, moyen, faible ou aucun), qui indique la gamme possible de longueur et de type de verrouillage d\'écran. L\'application peut aussi suggérer aux utilisateurs de mettre à jour l\'écran de verrouillage afin d\'utiliser un certain niveau de complexité, mais ils peuvent ignorer la suggestion. Notez que le verrouillage d\'écran n\'est pas stocké en texte brut pour de manière à ce que l\'application n\'ait pas accès au mot de passe exact."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"utiliser le matériel biométrique"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permet à l\'application d\'utiliser du matériel biométrique pour l\'authentification"</string>
- <string name="permlab_manageFingerprint" msgid="7432667156322821178">"gérer le matériel d\'empreinte digitale"</string>
+ <string name="permlab_manageFingerprint" msgid="7432667156322821178">"gérer le lecteur d\'empreintes digitales"</string>
<string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Permet à l\'application de faire appel à des méthodes d\'ajout et de suppression de modèles d\'empreinte digitale que vous pouvez utiliser."</string>
- <string name="permlab_useFingerprint" msgid="1001421069766751922">"utiliser le matériel d\'empreinte digitale"</string>
- <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permet à l\'application d\'utiliser du matériel d\'empreinte digitale pour l\'authentification"</string>
+ <string name="permlab_useFingerprint" msgid="1001421069766751922">"utiliser le lecteur d\'empreintes digitales"</string>
+ <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permet à l\'application d\'utiliser le lecteur d\'empreintes digitales pour l\'authentification"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"modifier votre collection de musique"</string>
<string name="permdesc_audioWrite" msgid="8057399517013412431">"Autorise l\'application à modifier votre collection de musique."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"modifier votre collection de vidéos"</string>
@@ -590,7 +590,7 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Matériel d\'empreinte digitale numérique indisponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Lecteur d\'empreintes digitales indisponible."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte digitale"</string>
<string name="fingerprint_error_timeout" msgid="2946635815726054226">"Le temps attribué pour lire l\'empreinte digitale est écoulé. Veuillez réessayer."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale numérique annulée."</string>
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Touchez pour supprimer votre modèle facial, puis ajoutez votre visage de nouveau"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pour utiliser le déverrouillage par reconnaissance faciale, activez l\'"<b>"accès à l\'appareil photo"</b>" dans Paramètres > Confidentialité"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Touchez pour ajouter une empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller la tablette ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Surveillez le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouillez votre appareil Android TV ou effacez toutes les données qu\'il contient en cas d\'un nombre trop élevé de tentatives."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Surveillez le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouillez le système d\'infodivertissement ou effacez toutes ses données en cas d\'un nombre trop élevé de tentatives."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouille le téléphone ou efface toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Surveille le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouille la tablette ou efface toutes les données de l\'utilisateur en cas d\'un nombre trop élevé de tentatives."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Surveillez le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouillez votre appareil Android TV ou effacez toutes les données de l\'utilisateur en cas d\'un nombre trop élevé de tentatives."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Surveillez le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouillez le système d\'infodivertissement ou effacez toutes les données de ce profil en cas d\'un nombre trop élevé de tentatives."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Surveille le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouille le téléphone ou efface toutes les données de l\'utilisateur en cas d\'un nombre trop élevé de tentatives."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Modifier le verrouillage de l\'écran"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Modifier le verrouillage de l\'écran."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Effacer toutes les données"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Effacer les données de la tablette sans avertissement, en rétablissant les paramètres par défaut"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Effacez les données de votre appareil Android TV sans avertissement en effectuant une réinitialisation des paramètres d\'usine."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Effacez les données du système d\'infodivertissement sans avertissement en rétablissant les paramètres par défaut."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Effacer les données du téléphone sans avertissement en rétablissant les paramètres par défaut."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Effacer les données de l\'utilisateur"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Effacer les données de profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Effacer les données de l\'utilisateur"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Effacer les données de l\'utilisateur sur cette tablette sans avertissement."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Effacez les données de cet utilisateur sur cet appareil Android TV sans avertissement."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Effacez les données de ce profil sur ce système d\'infodivertissement sans avertissement."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Effacer les données de l\'utilisateur sur ce téléphone sans avertissement."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Définir le serveur mandataire global du mobile"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Indiquer le mandataire global à utiliser pour l\'appareil lorsque la politique est activée. Seul le propriétaire de l\'appareil peut définir le mandataire global."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 59046ee..5031ec7 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Appuyez pour supprimer votre empreinte faciale, puis ajoutez de nouveau votre visage"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pour utiliser Face Unlock, activez "<b>"Accès à l\'appareil photo"</b>" dans Paramètres > Confidentialité"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Appuyez pour ajouter une empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller la tablette ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Contrôle le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouille votre appareil Android TV ou en efface toutes les données si le nombre maximal de mots de passe incorrects autorisé est dépassé."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le système d\'infoloisirs ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le téléphone ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Contrôlez le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouillez la tablette ou effacez toutes les informations sur l\'utilisateur si le nombre maximal de mots de passe incorrects autorisés est dépassé."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Contrôle le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouille votre appareil Android TV ou efface toutes les données de cet utilisateur si le nombre maximal de mots de passe incorrects autorisé est dépassé."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le système d\'infoloisirs ou effacer toutes les données de ce profil si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Contrôlez le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouillez le téléphone ou effacez toutes les informations sur l\'utilisateur si le nombre maximal de mots de passe incorrects autorisés est dépassé."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Modifier le verrouillage de l\'écran"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Modifier le verrouillage de l\'écran"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Effacer toutes les données"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Effacer les données de la tablette sans avertissement, en rétablissant la configuration d\'usine"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Efface les données de votre appareil Android TV sans avertissement en rétablissant la configuration d\'usine."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Effacer les données du système d\'infoloisirs sans avertissement en rétablissant la configuration d\'usine."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Effacer les données du téléphone sans avertissement, en rétablissant la configuration d\'usine"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Effacer les informations sur l\'utilisateur"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Effacer les données du profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Effacer les informations sur l\'utilisateur"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Effacer les informations sur cet utilisateur de cette tablette sans avertissement"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Efface les données concernant cet utilisateur de cet appareil Android TV sans avertissement."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Effacer les données de ce profil sur ce système d\'infoloisirs sans avertissement."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Effacer les informations sur cet utilisateur de ce téléphone sans avertissement"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Définir le proxy global du mobile"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Indiquer le proxy global à utiliser pour l\'appareil lorsque la règle est activée. Seul le propriétaire de l\'appareil peut définir le proxy global."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 34f027c..ae73ee1 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca para eliminar o teu modelo facial e despois engade de novo a cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mira o teléfono para desbloquealo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o desbloqueo facial, activa "<b>"Acceso á cámara"</b>" en Configuración > Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura máis maneiras de desbloquear o dispositivo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para engadir unha impresión dixital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo mediante impresión dixital"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Controlar os intentos de desbloqueo da pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Supervisa o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquea a tableta ou borra todos os datos da tableta se se escriben demasiados contrasinais incorrectos."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Controla o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquea o dispositivo Android TV ou borra todos os datos do dispositivo se se escriben demasiados contrasinais incorrectos."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Supervisa o número de contrasinais incorrectos que se escriben ao desbloquear a pantalla e bloquea o sistema de información e entretemento ou borra todos os seus datos se se meten demasiados incorrectos."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisa o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquea o teléfono ou borra todos os datos do teléfono se se escriben demasiados contrasinais incorrectos."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Supervisar o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquear a tableta ou borrar todos os datos do usuario se se escriben demasiados contrasinais incorrectos."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Controla o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquea o dispositivo Android TV ou borra todos os datos deste usuario se se escriben demasiados contrasinais incorrectos."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Supervisa o número de contrasinais incorrectos que se escriben ao desbloquear a pantalla e bloquea o sistema de información e entretemento ou borra todos os datos deste perfil se se meten demasiados incorrectos."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Supervisar o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquear o teléfono ou borrar todos os datos do usuario se se escriben demasiados contrasinais incorrectos."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Cambiar o bloqueo da pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Cambia o bloqueo da pantalla."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Borrar todos os datos"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Borra os datos da tableta sen previo aviso mediante a realización dun restablecemento dos datos de fábrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Borra a información do dispositivo Android TV sen avisar e realiza un restablecemento dos datos de fábrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Borra os datos do sistema de información e entretemento sen previo aviso a través do restablecemento dos datos de fábrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Borra os datos do teléfono sen previo aviso mediante a realización dun restablecemento dos datos de fábrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Borrar os datos do usuario"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Borrar os datos do perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Borrar os datos do usuario"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Borra os datos deste usuario nesta tableta sen previo aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Borra os datos deste usuario neste dispositivo Android TV sen avisar."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Borra os datos deste perfil do sistema de información e entretemento sen previo aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Borra os datos deste usuario neste teléfono sen previo aviso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Establecer o proxy global do dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Configura o proxy global do dispositivo que se debe usar cando a política está activada. Só o propietario do dispositivo pode configurar o proxy global."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 45934d2..ed24c5a 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"તમારા ચહેરાનું મૉડલ ડિલીટ કરવા માટે ટૅપ કરો, પછી તમારો ચહેરો ફરીથી ઉમેરો"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ફેસ અનલૉક સુવિધાનું સેટઅપ કરો"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"તમારા ફોનની તરફ જોઈને તેને અનલૉક કરો"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ફેસ અનલૉક સુવિધાનો ઉપયોગ કરવા માટે, સેટિંગ > પ્રાઇવસીમાં જઈને "<b>"કૅમેરા ઍક્સેસ"</b>" ચાલુ કરો"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"અનલૉક કરવાની બીજી રીતોનું સેટઅપ કરો"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ફિંગરપ્રિન્ટ ઉમેરવા માટે ટૅપ કરો"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ફિંગરપ્રિન્ટ અનલૉક"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"સ્ક્રીનને અનલૉક કરવાના પ્રયત્નોનું નિયમન કરો"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ટેબ્લેટને લૉક કરો અથવા ટેબ્લેટનો તમામ ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"સ્ક્રીનને અનલૉક કરતી વખતે ટાઇપ કરેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ ટાઇપ કર્યા હોય, તો તમારા Android TV ડિવાઇસના ડેટાને લૉક કરો અથવા આ વપરાશકર્તાનો બધો ડેટા કાઢી નાખો."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ લખ્યા હોય તો ઇન્ફોટેનમેન્ટ સિસ્ટમને લૉક કરો અથવા ઇન્ફોટેનમેન્ટ સિસ્ટમનો બધો ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ફોનને લૉક કરો અથવા ફોનનો તમામ ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ટેબ્લેટને લૉક કરો અથવા આ વપરાશકર્તાનો તમામ ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"સ્ક્રીનને અનલૉક કરતી વખતે ટાઇપ કરેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ ટાઇપ કર્યા હોય તો તમારા Android TV ડિવાઇસને લૉક કરો અથવા આ વપરાશકર્તાનો બધો ડેટા કાઢી નાખો."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ લખ્યા હોય તો ઇન્ફોટેનમેન્ટ સિસ્ટમને લૉક કરો અથવા આ પ્રોફાઇલનો બધો ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ફોનને લૉક કરો અથવા આ વપરાશકર્તાનો તમામ ડેટા કાઢી નાખો."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"સ્ક્રીન લૉક બદલો"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"સ્ક્રીન લૉક બદલો."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"બધો ડેટા કાઢી નાખો"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ફેક્ટરી ડેટા ફરીથી સેટ કરોને કરીને ચેતવણી વિના ટેબ્લેટનો ડેટા કાઢી નાખો."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ફેક્ટરી ડેટા રીસેટ કરીને ચેતવણી વિના તમારા Android TV ડિવાઇસનો ડેટા કાઢી નાખો."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ફેક્ટરી ડેટા રીસેટ કરીને કોઈ ચેતવણી વિના જ ઇન્ફોટેનમેન્ટ સિસ્ટમનો ડેટા કાઢી નાખો."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ફેક્ટરી ડેટા ફરીથી સેટ કરોને કરીને ચેતવણી વિના ફોનનો ડેટા કાઢી નાખો."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"વપરાશકર્તા ડેટા કાઢી નાખો"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"પ્રોફાઇલનો ડેટા કાઢી નાખો"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"વપરાશકર્તા ડેટા કાઢી નાખો"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ચેતવણી વિના આ ટેબ્લેટ પરનો આ વપરાશકર્તાનો ડેટા કાઢી નાખો."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ચેતવણી વિના આ Android TV ડિવાઇસ પર રહેલો આ વપરાશકર્તાનો ડેટા કાઢી નાખો."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"કોઈ ચેતવણી વિના જ આ ઇન્ફોટેનમેન્ટ સિસ્ટમ પર રહેલો આ પ્રોફાઇલનો ડેટા કાઢી નાખો."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ચેતવણી વિના આ ફોન પરનો આ વપરાશકર્તાનો ડેટા કાઢી નાખો."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ઉપકરણ વૈશ્વિક પ્રોક્સી સેટ કરો"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"પૉલિસી ચાલુ હોય તે વખતે ઉપયોગ કરવા માટેના ડિવાઇસ વૈશ્વિક પ્રોક્સીને સેટ કરો. ફક્ત ડિવાઇસના માલિક વૈશ્વિક પ્રોક્સી સેટ કરી શકે છે."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 81251f1..f780957 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"अपने चेहरे का मॉडल मिटाने के लिए टैप करें. इसके बाद, अपना चेहरा फिर से रजिस्टर करें"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फे़स अनलॉक की सुविधा सेट अप करें"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"अपने फ़ोन की तरफ़ देखकर उसे अनलॉक करें"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फ़ेस अनलॉक की सुविधा का इस्तेमाल करने के लिए, सेटिंग और निजता में जाकर, "<b>"कैमरे का ऐक्सेस"</b>" चालू करें"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"फ़ोन को अनलॉक करने के दूसरे तरीके सेट अप करें"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फ़िंगरप्रिंट जोड़ने के लिए टैप करें"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फ़िंगरप्रिंट अनलॉक"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रीन अनलॉक करने के की कोशिशों पर नज़र रखना"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रीन को अनलॉक करते समय गलत लिखे गए पासवर्ड की संख्या पर निगरानी करें, और बहुत ज़्यादा बार गलत पासवर्ड लिखे जाने पर टैबलेट लॉक करें या टैबलेट का संपूर्ण डेटा मिटाएं."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने Android TV डिवाइस को तुरंत लॉक करें या इसका सभी डेटा मिटाएं."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो सूचना और मनोरंजन की सुविधा देने वाले डिवाइस को लॉक करें या इस डिवाइस का सारा डेटा मिटाएं."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रीन को अनलॉक करते समय जितनी बार गलत पासवर्ड लिखा गया है, उसकी संख्या पर नज़र रखना और अगर बहुत बार गलत पासवर्ड डाले गए हैं, तो फ़ोन को लॉक कर देना या फ़ोन का सारा डेटा मिटा देना."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रीन का लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार ज़्यादा पासवर्ड लिखे जाते हैं तो टैबलेट को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने Android TV डिवाइस को तुरंत लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटाएं."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो सूचना और मनोरंजन की सुविधा देने वाले डिवाइस को लॉक करें या इस प्रोफ़ाइल का सारा डेटा मिटाएं."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"स्क्रीनका लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार गलत पासवर्ड लिखा जाता है तो फ़ोन को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"स्क्रीन लॉक बदलना"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"इससे स्क्रीन लॉक बदला जाता है."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"सारा डेटा मिटाना"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"फ़ैक्टरी डेटा रीसेट करके चेतावनी दिए बिना फ़ोन का डेटा मिटाना."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ़ैक्ट्री डेटा रीसेट करके अपने Android TV डिवाइस का डेटा बिना चेतावनी दिए मिटाएं."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"फ़ैक्ट्री डेटा रीसेट करके, बिना किसी चेतावनी के सूचना और मनोरंजन की सुविधा देने वाले डिवाइस में सेव डेटा को हमेशा के लिए मिटाएं."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"इससे फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना फ़ोन का डेटा मिट जाता है."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"उपयोगकर्ता डेटा मिटाएं"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफ़ाइल का डेटा मिटाना"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"उपयोगकर्ता डेटा मिटाएं"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"इस टैबलेट पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"बिना चेतावनी दिए, इस Android TV डिवाइस से उपयोगकर्ता का डेटा मिटाएं."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"बिना किसी चेतावनी के, सूचना और मनोरंजन की सुविधा देने वाले डिवाइस में मौजूद इस प्रोफ़ाइल का डेटा मिटाएं."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"इस फ़ोन पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"डिवाइस वैश्विक प्रॉक्सी सेट करें"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"नीति चालू होने के दौरान इस्तेमाल करने के लिए डिवाइस ग्लोबल प्रॉक्सी सेट करें. केवल डिवाइस का मालिक ही ग्लोबल प्रॉक्सी सेट कर सकता है."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e326f8f..cd4f19e 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -619,6 +619,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, a zatim ponovo dodajte svoje lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite opciju "<b>"Pristup kameri"</b>" u odjeljku Postavke > Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -732,9 +733,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadziri pokušaje otključavanja zaslona"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Nadziri broj netočnih zaporki unesenih pri otključavanju zaslona i zaključaj tabletno računalo ili izbriši sve podatke na njemu ako je uneseno previše netočnih zaporki."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava Android TV uređaj ili s njega briše sve podatke ako se unese previše netočnih zaporki."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava sustav za informiranje i zabavu ili briše sve njegove podatke ako se unese previše netočnih zaporki."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Nadzire broj netočno unesenih zaporki pri otključavanju zaslona i zaključava telefon ili briše sve podatke na telefonu ako je uneseno previše netočnih zaporki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava tablet ili briše sve podatke korisnika ako se unese previše netočnih zaporki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava Android TV uređaj ili briše sve podatke korisnika ako se unese previše netočnih zaporki."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava sustav za informiranje i zabavu ili briše sve podatke profila ako se unese previše netočnih zaporki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava telefon ili briše sve podatke korisnika ako se unese previše netočnih zaporki."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Mijenjanje zaporke za zaključavanje"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Mijenja se zaporka za zaključavanje zaslona."</string>
@@ -743,10 +746,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje svih podataka"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Vraćanjem u tvorničko stanje izbriši podatke tabletnog računala bez upozorenja."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Podatke Android TV uređaja izbrišite bez upozorenja vraćanjem uređaja na tvorničke postavke."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Briše podatke sustava za informiranje i zabavu bez upozorenja vraćanjem na tvorničko stanje."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Vraćanjem na tvorničke postavke brišu se podaci s telefona bez upozorenja."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Izbriši podatke korisnika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Izbriši podatke profila"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Izbriši podatke korisnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Briše podatke korisnika na ovom tabletu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Briše podatke korisnika na Android TV uređaju bez upozorenja."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Briše podatke profila na ovom sustavu za informiranje i zabavu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Briše podatke korisnika na ovom telefonu bez upozorenja."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"postavi globalni proxy uređaja"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Postavlja globalni proxy za uređaj koji će se upotrebljavati dok je pravilo omogućeno. Samo vlasnik uređaja može postaviti globalni proxy."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 567783d..ca51e10 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Koppintson arcmodellje törléséhez, majd készítsen újat"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Az Arcalapú feloldás beállítása"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Feloldhatja a zárolást úgy, hogy ránéz a telefonra"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Az Arcalapú feloldás funkció használatához kapcsolja be a "<b>"Hozzáférés a kamerához"</b>" beállítást a Beállítások > Adatvédelem szakaszban."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"További feloldási módszerek beállítása"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Koppintson ide ujjlenyomat hozzáadásához"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Feloldás ujjlenyomattal"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Képernyőzár-feloldási kísérletek figyelése"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Megfigyeli a képernyő feloldásakor helytelenül beírt jelszavak számát, és túl sok hibásan beírt jelszó esetén lezárja a táblagépet, vagy törli a táblagép összes adatát."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"A képernyő feloldásához megadott helytelen jelszavak számának figyelése, és az Android TV eszköz zárolása vagy az Android TV eszköz összes adatának törlése túl sok helytelen jelszó után."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Megfigyeli a képernyő feloldásakor helytelenül beírt jelszavak számát, és túl sok hibásan beírt jelszó esetén lezárja az infotainmentrendszert, vagy törli az infotainmentrendszer összes adatát."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Megfigyeli a képernyő feloldásakor helytelenül beírt jelszavak számát, és túl sok hibásan beírt jelszó esetén lezárja a telefont, vagy törli a telefon összes adatát."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"A képernyőzárolás során megadott helytelen jelszavak számának figyelése, és a táblagép zárolása vagy a felhasználó összes adatának törlése abban az esetben, ha túl sokszor adtak meg helytelen jelszót."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"A képernyő feloldásához megadott helytelen jelszavak számának figyelése, és az Android TV eszköz zárolása vagy ezen felhasználó összes adatának törlése túl sok helytelen jelszó után."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"A képernyőzárolás során megadott helytelen jelszavak számának figyelése, és az infotainmentrendszer zárolása vagy a profil összes adatának törlése abban az esetben, ha túl sokszor adtak meg helytelen jelszót."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"A képernyőzárolás során megadott helytelen jelszavak számának figyelése, és a telefon zárolása vagy a felhasználó összes adatának törlése abban az esetben, ha túl sokszor adtak meg helytelen jelszót."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"A képernyőzár módosítása"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"A képernyőzár módosítása."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Minden adat törlése"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Figyelmeztetés nélkül törli a táblagép adatait, visszaállítva a gyári adatokat."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Az Android TV eszköz adatainak figyelmeztetés nélküli törlése a gyári adatok visszaállításával."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Az infotainmentrendszer adatainak törlése a gyári alapbeállítások visszaállításával anélkül, hogy figyelmeztetés jelenne meg erről."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Figyelmeztetés nélkül törli a telefon összes adatát, visszaállítva a gyári adatokat."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"A felhasználói adatok törlése"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profiladatok törlése"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"A felhasználói adatok törlése"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"A felhasználó összes adatának törlése erről a táblagépről figyelmeztetés nélkül."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"A felhasználó összes adatának figyelmeztetés nélküli törlése erről az Android TV eszközről."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"A profil adatainak törlése erről az infotainmentrendszerről figyelmeztetés nélkül."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"A felhasználó összes adatának törlése erről a telefonról figyelmeztetés nélkül."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Az eszköz globális proxyjának beállítása"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Az eszköz globális proxyja lesz használatban, amíg a házirend engedélyezve van. A globális proxyt csak az eszköz tulajdonosa állíthatja be."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 87523a4..5c01197 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Հպեք՝ ձեր դեմքի նմուշը ջնջելու համար, այնուհետև նորից ավելացրեք այն:"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Կարգավորեք դեմքով ապակողպումը"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ապակողպելու համար պարզապես նայեք հեռախոսին"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Դեմքով ապակողպումն օգտագործելու համար անցեք Կարգավորումներ > Գաղտնիություն և տրամադրեք "<b>"տեսախցիկն օգտագործելու թույլտվություն"</b>"։"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Կարգավորեք ապակողպելու այլ եղանակներ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Հպեք՝ մատնահետք ավելացնելու համար"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Մատնահետքով ապակողպում"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Վերահսկել էկրանի ապակողպման փորձերը"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Վերահսկել սխալ գաղտնաբառերի թիվը, որոնք մուտքագրվել են էկրանն ապակողպելիս, և կողպել պլանշետը կամ ջնջել պլանշետի բոլոր տվյալները, եթե մուտքագրվել են չափից շատ սխալ գաղտնաբառեր:"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Գրանցել էկրանի ապակողպման համար մուտքագրվող սխալ գաղտնաբառերի թիվը և կողպել Android TV սարքը կամ ջնջել սարքի բոլոր տվյալները՝ չափից ավելի շատ սխալ գաղտնաբառեր մուտքագրելու դեպքում:"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Վերահսկել սխալ գաղտնաբառերի թիվը, որոնք մուտքագրվել են էկրանը ապակողպելիս, և կողպել տեղեկատվաժամանցային համակարգը կամ ջնջել բոլոր տվյալները, եթե չափից ավելի սխալ գաղտնաբառեր են մուտքագրվել։"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Վերահսկել սխալ գաղտնաբառերի թիվը, որոնք մուտքագրվել են էկրանն ապակողպելիս, և կողպել հեռախոսը կամ ջնջել հեռախոսի բոլոր տվյալները, եթե մուտքագրվել են չափից շատ սխալ գաղտնաբառեր:"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Կառավարել էկրանն ապակողպելիս մուտքագրվող սխալ գաղտնաբառերի թիվը և կողպել պլանշետը կամ ջնջել այս օգտատիրոջ բոլոր տվյալները չափից ավելի սխալ գաղտնաբառեր մուտքագրելու դեպքում:"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Գրանցել էկրանի ապակողպման համար մուտքագրվող սխալ գաղտնաբառերի թիվը և կողպել Android TV սարքը կամ ջնջել այս օգտատիրոջ բոլոր տվյալները՝ չափից ավելի շատ սխալ գաղտնաբառեր մուտքագրելու դեպքում:"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Վերահսկել սխալ գաղտնաբառերի թիվը, որոնք մուտքագրվել են էկրանը ապակողպելիս, և կողպել տեղեկատվաժամանցային համակարգը կամ ջնջել այս պրոֆիլի բոլոր տվյալները, եթե չափից ավելի սխալ գաղտնաբառեր են մուտքագրվել։"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Կառավարել էկրանն ապակողպելիս մուտքագրվող սխալ գաղտնաբառերի թիվը և կողպել հեռախոսը կամ ջնջել այս օգտատիրոջ բոլոր տվյալները չափից ավելի սխալ գաղտնաբառեր մուտքագրելու դեպքում:"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Փոխել էկրանի կողպման գաղտնաբառը"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Փոխել էկրանի կողպման գաղտնաբառը:"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Ջնջել բոլոր տվյալները"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ջնջել պլանշետի տվյալներն առանց նախազգուշացման` կատարելով գործարանային տվյալների վերակայում:"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ջնջել Android TV սարքի տվյալներն առանց զգուշացման՝ վերականգնելով գործարանային կարգավորումները:"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ջնջել տեղեկատվաժամանցային համակարգի տվյալները առանց զգուշացման՝ վերականգնելով գործարանային կարգավորումները։"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ջնջել հեռախոսի տվյալներն առանց նախազգուշացման` կատարելով գործարանային տվյալների վերակայում:"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Ջնջել օգտատիրոջ տվյալները"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Պրոֆիլի տվյալների ջնջում"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Ջնջել օգտատիրոջ տվյալները"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ջնջել այս օգտատիրոջ տվյալներն այս պլանշետում առանց նախազգուշացման:"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ջնջել այս օգտատիրոջ տվյալներն Android TV սարքում առանց նախազգուշացման:"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ջնջել տեղեկատվաժամանցային համակարգի այս պրոֆիլի տվյալները առանց զգուշացման։"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ջնջել այս օգտատիրոջ տվյալներն այս հեռախոսում առանց նախազգուշացման:"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Կարգավորել սարքի համաշխարհային պրոքսին"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Կարգավորել, որ սարքի համընդհանուր պրոքսի-սերվերն օգտագործվի, երբ քաղաքականությունը միացված է: Միայն սարքի սեփականատերը կարող է կարգավորել համընդհանուր պրոքսի-սերվերը:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 2a2762a..b4cd162 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketuk untuk menghapus model wajah, lalu tambahkan wajah Anda lagi"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Siapkan Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci ponsel dengan melihat ke ponsel"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Face Unlock, aktifkan "<b>"Akses kamera"</b>" di Setelan > Privasi"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Siapkan lebih banyak cara untuk membuka kunci"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketuk untuk menambahkan sidik jari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau upaya pembukaan kunci layar"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci tablet atau menghapus semua data tablet jika terjadi terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci perangkat Android TV atau menghapus semua data perangkat Android TV jika terlalu banyak sandi salah diketikkan."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci sistem infotainmen atau menghapus semua data sistem infotainmen jika terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci ponsel atau menghapus semua data ponsel jika terjadi terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci tablet atau menghapus semua data pengguna ini jika terjadi terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci perangkat Android TV atau menghapus semua data pengguna ini jika terlalu banyak sandi salah diketikkan."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci sistem infotainmen atau menghapus semua data profil ini jika terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci ponsel atau menghapus semua data pengguna ini jika terjadi terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Mengubah kunci layar"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Mengubah kunci layar."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Menghapus semua data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Menghapus data tablet tanpa peringatan dengan mereset ke setelan pabrik."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Menghapus data perangkat Android TV tanpa peringatan dengan melakukan reset ke setelan pabrik."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Menghapus data sistem infotainmen tanpa peringatan dengan melakukan reset ke setelan pabrik."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Menghapus data ponsel tanpa peringatan dengan melakukan reset ke setelan pabrik."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Menghapus data pengguna"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Hapus data profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Menghapus data pengguna"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Menghapus data pengguna ini di tablet ini tanpa peringatan."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Menghapus data pengguna ini di perangkat Android TV ini tanpa peringatan."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Menghapus data profil ini di sistem infotainmen ini tanpa peringatan."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Menghapus data pengguna ini di ponsel ini tanpa peringatan."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Setel proxy global perangkat"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Menyetel proxy global perangkat yang akan digunakan jika kebijakan diaktifkan. Hanya pemilik perangkat yang dapat menyetel proxy global."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 8919bf7..7e4d8e5 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ýttu til að eyða andlitslíkaninu og skráðu svo andlitið aftur"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Setja upp andlitskenni"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Taktu símann úr lás með því að horfa á hann"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Þú verður að kveikja á "<b>"aðgangi að myndavél"</b>" í „Stillingar > persónuvernd“ til að nota andlitskenni"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Settu upp fleiri leiðir til að taka úr lás"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ýttu til að bæta við fingrafari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingrafarskenni"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Fylgjast með tilraunum til að taka skjáinn úr lás"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa spjaldtölvunni eða eyða öllum gögnum hennar ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa Android TV tækinu eða eyða öllum gögnum tækisins ef rangt aðgangsorð er fært inn of oft."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa upplýsinga- og afþreyingarkerfinu eða eyða öllum gögnum upplýsinga- og afþreyingarkerfisins ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa símanum eða eyða öllum gögnum hans ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa spjaldtölvunni eða eyða öllum gögnum viðkomandi notanda ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa Android TV eða eyða öllum gögnum viðkomandi notanda ef rangt aðgangsorð er fært inn of oft."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa upplýsinga- og afþreyingarkerfinu eða eyða öllum gögnum þessa prófíls ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa símanum eða eyða öllum gögnum viðkomandi notanda ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Breyta skjálásnum"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Breyta skjálásnum."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Eyða öllum gögnum"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Eyða gögnum spjaldtölvunnar fyrirvaralaust með núllstillingu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Eyða gögnum Android TV tækisins án viðvörunar með því að núllstilla það."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Eyða gögnum upplýsinga- og afþreyingarkerfisins án viðvörunar með núllstillingu."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Eyða gögnum símans fyrirvaralaust með núllstillingu."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Eyða gögnum notanda"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Eyða prófílgögnum"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Eyða gögnum notanda"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Eyða gögnum viðkomandi notanda í þessari spjaldtölvu án viðvörunar."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Eyða gögnum þessa notanda úr þessu Android TV tæki án viðvörunar."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Eyða gögnum þessa prófíls í þessu upplýsinga- og afþreyingarkerfi án viðvörunar."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Eyða gögnum viðkomandi notanda í þessum síma án viðvörunar."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Stilla altækan proxy-þjón fyrir tækið"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Velja altækan proxy-þjón tækisins sem nota á þegar stefnan er virk. Aðeins eigandi tækisins getur valið altækan proxy-þjón."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4f5de40..d60fdae 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tocca per eliminare il tuo modello del volto e poi riaggiungi il tuo volto"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura lo sblocco con il volto"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Sblocca il telefono guardandolo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Per utilizzare lo sblocco con il volto, attiva "<b>"l\'accesso alla fotocamera"</b>" in Impostazioni > Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura altri modi per sbloccare"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tocca per aggiungere un\'impronta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sblocco con l\'impronta"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorare tentativi di sblocco dello schermo"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il tablet o cancella tutti i dati del tablet se vengono digitate troppe password errate."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Consente di monitorare il numero di password errate digitate durante lo sblocco dello schermo e di bloccare il dispositivo Android TV o cancellare tutti i dati del dispositivo se vengono digitate troppe password errate."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il sistema di infotainment o cancella tutti i dati del sistema se vengono digitate troppe password errate."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il telefono o cancella tutti i dati del telefono se vengono digitate troppe password errate."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il tablet o resetta tutti i dati dell\'utente se è stato raggiunto il limite massimo consentito."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Consente di monitorare il numero di password errate digitate durante lo sblocco dello schermo e di bloccare il dispositivo Android TV o cancellare tutti i dati dell\'utente se vengono digitate troppe password errate."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il sistema di infotainment o cancella tutti i dati di questo profilo se vengono digitate troppe password errate."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il telefono o resetta tutti i dati dell\'utente se è stato raggiunto il limite massimo consentito."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Modificare il blocco schermo"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Modifica il blocco schermo."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Cancellare tutti i dati"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Cancella i dati del tablet senza preavviso eseguendo un ripristino dati di fabbrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Consente di cancellare i dati del dispositivo Android TV senza preavviso eseguendo un ripristino dei dati di fabbrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Cancella i dati del sistema di infotainment senza preavviso eseguendo un ripristino dei dati di fabbrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Cancella i dati del telefono senza preavviso eseguendo un ripristino dei dati di fabbrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Resettare i dati dell\'utente"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Cancella dati del profilo"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Resettare i dati dell\'utente"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Resetta i dati dell\'utente sul tablet senza preavviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Consente di cancellare i dati dell\'utente su questo dispositivo Android TV senza preavviso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Cancella i dati di questo profilo su questo sistema di infotainment senza preavviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Resetta i dati dell\'utente sul telefono senza preavviso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Impostare il proxy globale del dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Imposta il proxy globale del dispositivo in modo da utilizzarlo mentre la norma è attiva. Il proxy globale può essere impostato solo dal proprietario del dispositivo."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 0aa6ad8..87415b0 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"יש להקיש כדי למחוק את התבנית לזיהוי הפנים, ואז להוסיף תבנית חדשה לזיהוי הפנים"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"הגדרת התכונה \'פתיחה ע\"י זיהוי הפנים\'"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"יש להביט בטלפון כדי לבטל את נעילתו"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"כדי להשתמש בתכונה \'פתיחה ע\"י זיהוי הפנים\', יש להפעיל את ה"<b>"גישה למצלמה"</b>" בהגדרות > פרטיות"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"אפשר להגדיר דרכים נוספות לביטול נעילה"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"יש להקיש כדי להוסיף טביעת אצבע"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ביטול הנעילה בטביעת אצבע"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"מעקב אחר ניסיונות לביטול של נעילת המסך"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ניהול מעקב אחר מספר הסיסמאות השגויות שמוקלדות במהלך ביטול נעילת המסך, וביצוע נעילה של הטאבלט, או מחיקה של כל נתוני הטאבלט, אם מוקלדות יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"מעקב אחר מספר הסיסמאות השגויות שהוזנו בעת ביטול נעילת המסך, כמו גם נעילה של מכשיר ה-Android TV או מחיקה של כל נתוני מכשיר ה-Android TV אם הוזנו יותר מדי סיסמאות שגויות."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"מעקב אחר מספר הסיסמאות השגויות שהוזנו במהלך ביטול נעילת המסך, נעילת מערכת המידע והבידור או מחיקה של כל נתוני מערכת המידע והבידור, אם הוזנו יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"מעקב אחר מספר הסיסמאות השגויות שהוקלדו במהלך ביטול נעילת המסך, וביצוע נעילה של הטלפון או מחיקה של כל נתוני הטלפון אם הוקלדו יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"מעקב אחר מספר הסיסמאות השגויות שהוזנו בעת ביטול נעילת המסך, כמו גם נעילת הטאבלט או מחיקה של כל נתוני המשתמש הזה אם הוזנו יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"מעקב אחר מספר הסיסמאות השגויות שהוזנו במהלך ביטול נעילת המסך, כמו גם נעילה של מכשיר ה-Android TV או מחיקה של כל נתוני המשתמש הזה אם הוזנו יותר מדי סיסמאות שגויות."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"מעקב אחר מספר הסיסמאות השגויות שהוזנו במהלך ביטול נעילת המסך, נעילת מערכת המידע והבידור או מחיקה של כל נתוני הפרופיל הזה, אם הוזנו יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"מעקב אחר מספר הסיסמאות השגויות שהוזנו בעת ביטול נעילת המסך, כמו גם נעילת הטלפון או מחיקה של כל נתוני המשתמש הזה אם הוזנו יותר מדי סיסמאות שגויות."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"שינוי נעילת המסך"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"שינוי של נעילת המסך."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"מחיקת כל הנתונים"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"מחיקה של נתוני הטאבלט ללא אזהרה, באמצעות איפוס לנתוני היצרן."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"מחיקה ללא אזהרה של נתוני מכשיר ה-Android TV באמצעות איפוס לנתוני היצרן."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"מחיקה של נתוני מערכת המידע והבידור, ללא אזהרה, על ידי ביצוע איפוס לנתוני היצרן."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"מחיקה של נתוני הטלפון, ללא אזהרה, על ידי ביצוע איפוס לנתוני היצרן."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"מחיקת נתוני משתמש"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"מחיקת נתוני הפרופיל"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"מחיקת נתוני משתמש"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"מחיקה של נתוני המשתמש הזה בטאבלט, ללא אזהרה."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"מחיקה של נתוני המשתמש הזה במכשיר ה-Android TV, ללא אזהרה."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"מחיקה של נתוני הפרופיל במערכת המידע והבידור הזו, ללא אזהרה."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"מחיקה של נתוני המשתמש הזה בטלפון, ללא אזהרה."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"הגדרה של שרת ה-Proxy הכללי של המכשיר"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"הגדרה של שרת ה-proxy הגלובלי שבו ייעשה שימוש כשהמדיניות פועלת. רק לבעלים של המכשיר יש אפשרות להגדיר את שרת ה-proxy הגלובלי."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 739e19e..004f3d1 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"タップして顔モデルを削除してから、改めて顔を追加してください"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"顔認証の設定"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"スマートフォンに顔を向けるとロックが解除されます"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"顔認証を使用するには、[設定] > [プライバシー] で"<b>"カメラへのアクセス"</b>"を有効にしてください"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"その他のロック解除方法の設定"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"タップすると指紋が追加されます"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋認証"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"画面ロック解除試行の監視"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はタブレットをロックするかタブレットのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合は Android TV デバイスをロックするか Android TV デバイスのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はインフォテインメント システムをロックするかインフォテインメント システムのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はモバイルデバイスをロックするかモバイルデバイスのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合はタブレットをロックするかこのユーザーのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合は Android TV デバイスをロックするかこのユーザーのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はインフォテインメント システムをロックするかこのプロファイルのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合はスマートフォンをロックするかこのユーザーのデータをすべて消去します。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"画面ロックの変更"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"画面ロックを変更します。"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"すべてのデータを消去"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"警告せずにタブレットを初期化して内部のデータを消去します。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"警告せずに出荷時設定へのリセットを実行して Android TV デバイスのデータを消去します。"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"警告せずにインフォテインメント システムを初期化して内部のデータを消去します。"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"警告せずにデバイスを初期化して内部のデータを消去します。"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ユーザーデータの消去"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"プロファイル データの消去"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ユーザーデータの消去"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"このタブレットのこのユーザーのデータを警告なく消去します。"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"この Android TV デバイスでこのユーザーのデータを警告なく消去します。"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"警告せずにこのインフォテインメント システムにあるこのプロファイルのデータを消去します。"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"このスマートフォンのこのユーザーのデータを警告なく消去します。"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"デバイスのグローバルプロキシを設定"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ポリシーが有効になっている場合はデバイスのグローバルプロキシが使用されるように設定します。グローバルプロキシを設定できるのはデバイスの所有者だけです。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 3ecbdbb..9f93c7d 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"შეეხეთ თქვენი სახის მოდელის წასაშლელად, შემდეგ დაამატეთ სახე ხელახლა"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"სახით განბლოკვის პარამეტრების დაყენება"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"განბლოკეთ თქვენი ტელეფონი შეხედვით"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"იმისთვის, რომ სახით განბლოკვით ისარგებლოთ, ჩართეთ "<b>"კამერაზე წვდომა"</b>" პარამეტრებსა და კონფიდენციალურობაში"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"დააყენეთ განბლოკვის სხვა ხერხები"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"შეეხეთ თითის ანაბეჭდის დასამატებლად"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"თითის ანაბეჭდით განბლოკვა"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ეკრანის განბლოკვის მცდელობების მონიტორინგი"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ეკრანის განბლოკვისთვის არასწორად აკრეფილი პაროლების რაოდენობის მონიტორინგი. ტაბლეტის დაბლოკვა ან მასზე არსებული ყველა მონაცემის წაშლა ძალიან ბევრჯერ არასწორი პაროლის შეყვანის შემთხვევაში."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ეკრანის განბლოკვისას არასწორი პაროლების შეყვანილი რაოდენობის მონიტორინგი და არასწორი პაროლის მეტისმეტად ბევრჯერ შეყვანის შემთხვევაში, Android TV მოწყობილობის დაბლოკვა ან Android TV მოწყობილობის მთელი ინფორმაციის ამოშლა."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ეკრანის განბლოკვისას გააკონტროლეთ პაროლების არასწორად შეყვანის რაოდენობა და დაბლოკეთ გართობის/საინფორმაციო სისტემა ან წაშალეთ მისი ყველა მონაცემი იმ შემთხვევაში, თუ ძალიან ბევრჯერ მოხდა არასწორი პაროლის შეყვანა."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ეკრანის განბლოკვისთვის არასწორად აკრეფილი პაროლების რაოდენობის მონიტორინგი. ტელეფონის დაბლოკვა ან მასზე არსებული ყველა მონაცემის წაშლა ძალიან ბევრჯერ არასწორი პაროლის შეყვანის შემთხვევაში."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ეკრანის განბლოკვისას არასწორი პაროლების შეყვანილი რაოდენობის მონიტორინგი და ტაბლეტის დაბლოკვა ან მრავლალჯერ არასწორი პაროლის შეყვანის შემთხვევაში ამ მომხმარებლის მთელი ინფორმაციის წაშლა."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ეკრანის განბლოკვისას არასწორი პაროლების შეყვანილი რაოდენობის მონიტორინგი და არასწორი პაროლის მეტისმეტად ბევრჯერ შეყვანის შემთხვევაში, Android TV მოწყობილობის დაბლოკვა ან ამ მომხმარებლის მთელი ინფორმაციის ამოშლა."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ეკრანის განბლოკვისას გააკონტროლეთ პაროლების არასწორად შეყვანის რაოდენობა და დაბლოკეთ გართობის/საინფორმაციო სისტემა ან წაშალეთ ამ პროფილის ყველა მონაცემი იმ შემთხვევაში, თუ ძალიან ბევრჯერ მოხდა არასწორი პაროლის შეყვანა."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ეკრანის განბლოკვისას არასწორი პაროლების შეყვანილი რაოდენობის მონიტორინგი და ტელეფონის დაბლოკვა ან მრავლალჯერ არასწორი პაროლის შეყვანის შემთხვევაში ამ მომხმარებლის მთელი ინფორმაციის წაშლა."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ეკრანის დაბლოკვის შეცვლა"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ეკრანის დაბლოკვის შეცვლა"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ყველა მონაცემის წაშლა"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ტაბლეტის მონაცემების გაუფრთხილებლად წაშლა, ქარხნული მონაცემების აღდგენით"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"თქვენი Android TV მოწყობილობის მონაცემების გაუფრთხილებლად ამოშლა ქარხნული მონაცემების აღდგენის გზით."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ქარხნული მონაცემების აღდგენით წაშალეთ გართობის/საინფორმაციო სისტემის მონაცემები გაფრთხილების გარეშე."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ტელეფონის მონაცემების გაუფრთხილებლად წაშლა, ქარხნული მონაცემების აღდგენით"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"მომხმარებლის მონაცემების წაშლა"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"პროფილის მონაცემების წაშლა"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"მომხმარებლის მონაცემების წაშლა"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ამ მომხმარებლის მონაცემების გაუფრთხილებელი წაშლა ამ ტაბლეტზე."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"მომხმარებლის მონაცემების გაუფრთხილებლად ამოშლა Android TV მოწყობილობიდან."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ქარხნული მონაცემების აღდგენით წაშალეთ ამ პროფილის მონაცემები ამ გართობ/საინფორმაციო სისტემაში გაფრთხილების გარეშე."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ამ მომხმარებლის მონაცემების გაუფრთხილებელი წაშლა ამ ტელეფონზე."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"მოწყობილობის გლობალური პროქსის დაყენება"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ჩართული პოლიტიკის დროს მოწყობილობის გლობალური პროქსის დაყენება. მხოლოდ მოწყობილობის მფლობელს შეუძლია გლობალური პროქსის დაყენება."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 9498980..e552a5f 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Бет үлгісін жою үшін түртіңіз, содан соң жаңа бет үлгісін қосыңыз."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Бет тану функциясын реттеу"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефоныңызға қарап, оның құлпын ашыңыз."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Face Unlock функциясын пайдалану үшін \"Параметрлер > Құпиялылық\" бөлімінен "<b>"Камераны пайдалану рұқсатын"</b>" қосыңыз."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Құлыпты ашудың басқа тәсілдерін реттеу"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Саусақ ізін қосу үшін түртіңіз."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Құлыпты саусақ ізімен ашу"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран құлпын ашу әркеттерін бақылау"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Экран бекітпесін ашқан кезде терілген қате құпия сөздердің санын бақылау және планшетті бекіту немесе тым көп қате құпия сөздер терілген болса, планшеттің бүкіл деректерін өшіру."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Экранның құлпын ашу кезінде қате енгізілген құпия сөздердің санын бақылау, құпия сөз тым көп қате енгізілген жағдайда, Android TV құрылғысын құлыптау және Android TV құрылғыңыздың барлық деректерінен тазарту."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Экран құлпын ашқан кезде, терілген қате құпия сөздердің саны бақыланады, сондай-ақ құпия сөздер бірнеше рет қате терілсе, ақпараттық-сауықтық жүйе құлыпталады немесе оның барлық дерегі жойылады."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Экран бекітпесін ашқан кезде терілген қате құпия сөздердің санын бақылау және телефонды бекіту немесе тым көп қате құпия сөздер терілген болса, телефонның бүкіл деректерін өшіру."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Экран бекітпесін ашқанда терілген қате құпия сөздердің санын бақылау және тым көп қате құпия сөздер терілсе, планшетті бекіту немесе осы пайдаланушының барлық деректерін өшіру."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Экранның құлпын ашу кезінде қате енгізілген құпия сөздердің санын бақылау, құпия сөз тым көп қате енгізілген жағдайда, Android TV құрылғысын құлыптау және барлық пайдаланушы деректерінен тазарту."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Экран құлпын ашқан кезде, терілген қате құпия сөздердің саны бақыланады, сондай-ақ құпия сөздер бірнеше рет қате терілсе, ақпараттық-сауықтық жүйе құлыпталады немесе осы профильдің барлық дерегі жойылады."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Экран бекітпесін ашқанда терілген қате құпия сөздердің санын бақылау және тым көп қате құпия сөздер терілсе, телефонды бекіту немесе осы пайдаланушының барлық деректерін өшіру."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Экран құлпын өзгерту"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Экран құлпын өзгерте алады."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Барлық деректерді өшіру"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Планшет дерекқорын ескертусіз, зауыттық дерекқорын қайта реттеу арқылы өшіру."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Зауыттық деректерді қалпына келтіру арқылы Android TV құрылғыңыздың деректерін ескертусіз тазартыңыз."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Зауыттық деректерді қалпына келтіру арқылы ақпараттық-сауықтық жүйе дерегі ескертусіз өшіріледі."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Зауыттық деректерге қайтару арқылы телефон деректерін ескертусіз өшіре алады."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Пайдаланушы деректерін өшіру"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Профиль дерегін өшіру"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Пайдаланушы деректерін өшіру"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Осы пайдаланушының осы планшеттегі деректерін ескертусіз өшіре алады."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Android TV құрылғысын осы пайдаланушы деректерінен ескертусіз тазартыңыз."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Осы ақпараттық-сауықтық жүйедегі профиль дерегі ескертусіз өшіріледі."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Осы пайдаланушының осы телефондағы деректерін ескертусіз өшіре алады."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Құрылғы жаһандық прокси қызметін орнату"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Саясат қосулы болғанда пайдаланылатын құрылғының ғаламдық прокси-серверін орнатыңыз. Ғаламдық прокси-серверді тек құрылғы иесі орната алады."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 841f5bf..7910a2b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ចុចដើម្បីលុបគំរូមុខរបស់អ្នក រួចបញ្ចូលមុខរបស់អ្នកម្ដងទៀត"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"រៀបចំការដោះសោតាមទម្រង់មុខ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ដោះសោទូរសព្ទរបស់អ្នកដោយសម្លឹងមើលវា"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ដើម្បីប្រើមុខងារដោះសោតាមទម្រង់មុខ សូមបើក"<b>"ការចូលប្រើកាមេរ៉ា"</b>"នៅក្នុងការកំណត់ > ឯកជនភាព"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"រៀបចំវិធីច្រើនទៀតដើម្បីដោះសោ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ចុចដើម្បីបញ្ចូលស្នាមម្រាមដៃ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ការដោះសោដោយប្រើស្នាមម្រាមដៃ"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"តាមដានការព្យាយាមដោះសោអេក្រង់"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ពិនិត្យចំនួនបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ។ ពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យទូរស័ព្ទទាំងអស់ ប្រសិនបើមានពាក្យសម្ងាត់បញ្ចូលមិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោឧបករណ៍ Android TV របស់អ្នក ឬលុបទិន្នន័យឧបករណ៍ Android TV របស់អ្នកទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ឬលុបទិន្នន័យទាំងអស់របស់ប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ពិនិត្យចំនួនបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ។ ពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យទូរស័ព្ទទាំងអស់ ប្រសិនបើមានពាក្យសម្ងាត់បញ្ចូលមិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ត្រួតពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោថេប្លេត ឬលុបទិន្នន័យអ្នកប្រើនេះទាំងអស់ ប្រសិនបើមានការវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោឧបករណ៍ Android TV របស់អ្នក ឬលុបទិន្នន័យរបស់អ្នកប្រើប្រាស់នេះទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ឬលុបទិន្នន័យទាំងអស់របស់កម្រងព័ត៌មាននេះ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ត្រួតពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យអ្នកប្រើនេះទាំងអស់ ប្រសិនបើមានការវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ប្តូរការចាក់សោអេក្រង់"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ប្តូរការចាក់សោអេក្រង់។"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"លុបទិន្នន័យទាំងអស់"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"លុបទិន្នន័យកុំព្យូទ័របន្ទះដោយមិនព្រមានដោយអនុវត្តការកំណត់ទិន្នន័យដូចចេញពីរោងចក្រ។"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"លុបទិន្នន័យឧបករណ៍ Android TV របស់អ្នកដោយមិនមានការព្រមាន ដោយធ្វើការកំណត់ទិន្នន័យដូចចេញពីរោងចក្រ។"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"លុបទិន្នន័យរបស់ប្រព័ន្ធព័ត៌មាន និងកម្សាន្តដោយមិនមានការព្រមាន ដោយធ្វើការកំណត់ទិន្នន័យដូចចេញពីរោងចក្រ។"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"លុបទិន្នន័យទូរសព្ទដោយមិនមានការព្រមានជាមុន ដោយអនុវត្តការកំណត់ទិន្នន័យដូចចេញពីរោងចក្រ ។"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"លុបទិន្នន័យរបស់អ្នកប្រើ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"លុបទិន្នន័យកម្រងព័ត៌មាន"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"លុបទិន្នន័យរបស់អ្នកប្រើ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"លុបទិន្នន័យរបស់អ្នកប្រើនេះនៅលើថេប្លេតនេះដោយគ្មានការព្រមាន។"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"លុបទិន្នន័យរបស់អ្នកប្រើប្រាស់នេះនៅលើឧបករណ៍ Android TV នេះដោយគ្មានការព្រមាន។"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"លុបទិន្នន័យរបស់កម្រងព័ត៌មាននេះនៅលើប្រព័ន្ធព័ត៌មាន និងកម្សាន្តនេះដោយមិនមានការព្រមាន។"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"លុបទិន្នន័យរបស់អ្នកប្រើនេះនៅលើទូរស័ព្ទនេះដោយគ្មានការព្រមាន។"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"កំណត់ប្រូកស៊ីសកលរបស់ឧបករណ៍"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"កំណត់ប្រូកស៊ីសកលឧបករណ៍ដើម្បីប្រើប្រាស់ ខណៈពេលដែលគោលការណ៍បើកដំណើរការ។ មានតែឧបករណ៍ម្ចាស់ប៉ុណ្ណោះអាចកំណត់ប្រូកស៊ីសកលនេះបាន។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 400b421..787ad48 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಅನ್ನು ಅಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ, ನಂತರ ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಅನ್ನು ಪುನಃ ಸೇರಿಸಿ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ಫೇಸ್ ಅನ್ಲಾಕ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಿ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ಫೋನ್ ಅನ್ನು ನೋಡುವ ಮೂಲಕ ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ಫೇಸ್ ಅನ್ಲಾಕ್ ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್ಗಳು > ಗೌಪ್ಯತೆ ಎಂಬಲ್ಲಿ "<b>"ಕ್ಯಾಮರಾ ಪ್ರವೇಶವನ್ನು"</b>" ಆನ್ ಮಾಡಿ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಹೆಚ್ಚಿನ ಮಾರ್ಗಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಸೇರಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ಲಾಕ್"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ಪರದೆಯ ಅನ್ಲಾಕ್ ಪ್ರಯತ್ನಗಳನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ, ಮತ್ತು ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದ್ದರೆ ಟ್ಯಾಬ್ಲೆಟ್ನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ Android TV ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡುತ್ತದೆ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದರೆ ನಿಮ್ಮ ಎಲ್ಲಾ Android TV ಸಾಧನದ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕುತ್ತದೆ."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ ಮತ್ತು ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂ ಅನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದ್ದರೆ ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಿದಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ, ಮತ್ತು ಫೋನ್ ಅನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದ್ದರೆ ಫೋನ್ನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ಟೈಪ್ ಮಾಡಲಾದ ತಪ್ಪಾಗಿರುವ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಲಾಗಿದ್ದರೆ ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಲಾದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ ಮತ್ತು ನಿಮ್ಮ Android TV ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಲಾಗಿದ್ದರೆ ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ ಮತ್ತು ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂ ಅನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದ್ದರೆ ಈ ಪ್ರೊಫೈಲ್ನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ಟೈಪ್ ಮಾಡಲಾದ ತಪ್ಪಾಗಿರುವ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಫೋನ್ ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಲಾಗಿದ್ದರೆ ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬದಲಾಯಿಸಿ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬದಲಾಯಿಸಿ."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರೀಸೆಟ್ ಅನ್ನು ನಿರ್ವಹಿಸುವ ಮೂಲಕ ಎಚ್ಚರಿಕೆಯನ್ನು ನೀಡದೆಯೇ ಟ್ಯಾಬ್ಲೆಟ್ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರೀಸೆಟ್ ಮಾಡುವ ಮೂಲಕ ಎಚ್ಚರಿಕೆ ಇಲ್ಲದೆ ನಿಮ್ಮ Android TV ಸಾಧನದ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕುತ್ತದೆ."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರೀಸೆಟ್ ಮಾಡುವ ಮೂಲಕ ಎಚ್ಚರಿಕೆ ಇಲ್ಲದೆ ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂನ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರೀಸೆಟ್ ಅನ್ನು ನಿರ್ವಹಿಸುವ ಮೂಲಕ ಎಚ್ಚರಿಕೆಯನ್ನು ನೀಡದೆಯೇ ಫೋನ್ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ಬಳಕೆದಾರ ಡೇಟಾ ಅಳಿಸಿ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ಪ್ರೊಫೈಲ್ನ ಡೇಟಾವನ್ನು ಅಳಿಸಿ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ಬಳಕೆದಾರ ಡೇಟಾ ಅಳಿಸಿ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ಯಾವುದೇ ಸೂಚನೆ ಇಲ್ಲದೆ ಈ ಟ್ಯಾಬ್ಲೆಟ್ನಲ್ಲಿ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ಎಚ್ಚರಿಕೆ ಇಲ್ಲದೆ ಈ Android TV ಸಾಧನದಲ್ಲಿನ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕುತ್ತದೆ."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ಎಚ್ಚರಿಕೆಯಿಲ್ಲದೆ ಈ ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂನಲ್ಲಿ ಈ ಪ್ರೊಫೈಲ್ನ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ಯಾವುದೇ ಸೂಚನೆ ಇಲ್ಲದೆ ಈ ಫೋನ್ನಲ್ಲಿ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ಸಾಧನವನ್ನು ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಗೆ ಹೊಂದಿಸಿ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ನೀತಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ ಬಳಸಬೇಕಾದ ಸಾಧನದ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಿ. ಸಾಧನದ ಮಾಲೀಕರು ಮಾತ್ರ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಬಹುದಾಗಿರುತ್ತದೆ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 78ec661..f65ba48 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"탭하여 얼굴 모델을 삭제한 후 다시 얼굴을 추가하세요"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"얼굴 인식 잠금 해제 설정"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"휴대전화의 화면을 응시하여 잠금 해제할 수 있습니다."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"얼굴 인식 잠금 해제를 사용하려면 설정 > 개인 정보 보호에서 "<b>"카메라 액세스"</b>"를 사용 설정하세요."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"다른 잠금 해제 방법 설정"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"지문을 추가하려면 탭하세요."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"지문 잠금 해제"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"화면 잠금 해제 시도 모니터링"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"화면 잠금해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고, 잘못된 비밀번호 입력 횟수가 너무 많은 경우 태블릿을 잠그거나 태블릿에 있는 데이터를 모두 지웁니다."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 Android TV 기기를 잠그거나 Android TV 기기의 데이터를 모두 삭제합니다."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"화면 잠금 해제 시 잘못된 비밀번호를 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 인포테인먼트 시스템을 잠그거나 인포테인먼트 시스템의 데이터를 모두 삭제합니다."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"화면 잠금해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고, 잘못된 비밀번호 입력 횟수가 너무 많은 경우 휴대전화를 잠그거나 휴대전화에 있는 데이터를 모두 지웁니다."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 태블릿을 잠그거나 이 사용자의 데이터를 모두 삭제합니다."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 Android TV 기기를 잠그거나 사용자 데이터를 모두 삭제합니다."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"화면 잠금 해제 시 잘못된 비밀번호를 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 인포테인먼트 시스템을 잠그거나 이 프로필의 데이터를 모두 삭제합니다."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 휴대전화를 잠그거나 이 사용자의 데이터를 모두 삭제합니다."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"화면 잠금 변경"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"화면 잠금을 변경합니다."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"모든 데이터 삭제"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"초기화를 수행하여 경고 없이 태블릿 데이터를 지웁니다."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"초기화를 실행하여 경고 없이 Android TV 기기의 데이터를 삭제합니다."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"초기화를 실행하여 인포테인먼트 시스템의 데이터를 경고 없이 삭제합니다."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"초기화를 수행하여 경고 없이 휴대전화 데이터를 지웁니다."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"사용자 데이터 삭제"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"프로필 데이터 삭제"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"사용자 데이터 삭제"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"이 태블릿에서 사용자의 데이터를 경고 없이 삭제합니다."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Android TV 기기에서 사용자 데이터를 경고 없이 삭제합니다."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"인포테인먼트 시스템에서 이 프로필의 데이터를 경고 없이 삭제합니다."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"이 휴대전화에서 사용자의 데이터를 경고 없이 삭제합니다."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"기기 전체 프록시 설정"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"정책이 사용 설정되어 있는 동안 사용될 기기 전체 프록시를 설정합니다. 기기 소유자만 전체 프록시를 설정할 수 있습니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index ef92207..b0095c3 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Жүзүңүздүн үлгүсүн өчүрүү үчүн басып, жаңы үлгүнү кошуңуз"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну жөндөө"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефонуңузду карап туруп эле кулпусун ачып алыңыз"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Жөндөөлөр > Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Кулпусун ачуунун көбүрөөк жолдорун жөндөңүз"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Манжа изин кошуу үчүн басыңыз"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Кулпуланган түзмөктү манжа изи менен ачуу"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран кулпусун ачуу аракеттерин көзөмөлдөө"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Экрандын кулпусу ачылып жатканда туура эмес терилген сырсөздөрдүн санын текшерип, эгер алардын саны өтө эле көп болсо, планшетти кулпулаңыз же планшеттеги бардык маалыматтарды тазалап салыңыз."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Экрандын кулпусун ачуу учурунда сырсөздөр канча жолу туура эмес терилгенин тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, Android TV түзмөгүңүздү кулпулап же Android TV түзмөгүңүздөгү бардык дайын-даректериңизди тазалап салуу."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Экрандын кулпусу ачылып жатканда туура эмес терилген сырсөздөрдүн саны текшерилип, эгер алардын саны өтө эле көп болсо, инфозоок тутуму кулпуланып же инфозоок тутумундагы бардык маалыматтар өчүрүлөт."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Экрандын кулпусу ачылып жатканда туура эмес терилген сырсөздөрдүн санын текшерип, эгер алардын саны өтө эле көп болсо, телефонду кулпулаңыз же телефондогу бардык маалыматтарды тазалап салыңыз."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Экрандын кулпусун ачуу учурунда туура эмес терилген сырсөздөрдү тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, планшетти кулпулап же бул колдонуучунун бардык дайындарын тазалап салуу."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Экрандын кулпусун ачуу учурунда сырсөздөр канча жолу туура эмес терилгенин тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, Android TV түзмөгүңүздү кулпулап же колдонуучунун бардык дайындарын тазалап салуу."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Экрандын кулпусун ачуу учурунда туура эмес терилген сырсөздөрдү тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, инфозоок тутуму кулпуланып же бул профилдин бардык дайындары өчүрүлөт."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Экрандын кулпусун ачуу учурунда туура эмес терилген сырсөздөрдү тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, телефонду кулпулап же бул колдонуучунун бардык дайындарын тазалап салуу."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Экран кулпусун өзгөртүү"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Экран кулпусун өзгөртөт."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Бардык маалыматты өчүрүү"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Алдын-ала эскертпестен, баштапкы абалга келтирүү функциясы менен планшеттеги бардык маалыматтарды өчүрөт."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Android TV түзмөгүңүздүн дайындарын эскертүүсүз кайра башынан жөндөө аркылуу тазалоо."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Алдын ала эскертпестен, баштапкы абалга келтирүү функциясы менен инфозоок тутумундагы бардык маалыматтар өчүрүлөт."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Алдын-ала эскертпестен, баштапкы абалга келтирүү функциясы менен телефондогу бардык маалыматтарды өчүрөт."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Колдонуучунун дайындарын тазалоо"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Профилдин маалыматын өчүрүү"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Колдонуучунун дайындарын тазалоо"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Бул колдонуучунун ушул планшеттеги дайындарын эскертүүсүз тазалоо."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Бул Android TV түзмөгүндөгү бул колдонуучу дайындарын эскертүүсүз тазалоо."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Алдын ала эскертпестен, бул инфозоок тутумундагы профилдин бардык маалыматтары өчүрүлөт."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Бул колдонуучунун ушул телефондогу дайындарын эскертүүсүз тазалоо."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Түзмөктүн глобалдык проксисин коюу"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Саясат иштетилгенде түзмөктүн глобалдык проксиси колдонулгудай кылып коюңуз. Түзмөк ээси гана глобалдык проксини коё алат."</string>
@@ -1243,7 +1249,7 @@
<string name="screen_compat_mode_hint" msgid="4032272159093750908">"Муну тутум жөндөөлөрүнөн кайра иштетүү > Колдонмолор > Жүктөлүп алынган."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу көрүнүштүн тандалган өлчөмүн экранда көрсөтө албайт жана туура эмес иштеши мүмкүн."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Ар дайым көрүнсүн"</string>
- <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android OS тутуму менен иштеген түзмөктүн шайкеш келбеген версиясы үчүн орнотулган колдонмо жана туура эмес иштеши мүмкүн. Колдонмонун жаңыртылган версиясы жеткиликтүү болушу мүмкүн."</string>
+ <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android OS тутуму менен иштеген түзмөктүн шайкеш келбеген версиясы үчүн орнотулган колдонмо жана туура эмес иштеши мүмкүн. Колдонмонун жаңырган версиясы жеткиликтүү болушу мүмкүн."</string>
<string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Ар дайым көрүнсүн"</string>
<string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Жаңыртууларды текшерүү"</string>
<string name="smv_application" msgid="3775183542777792638">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу (<xliff:g id="PROCESS">%2$s</xliff:g> процесси) өз алдынча иштеткен StrictMode саясатын бузду."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 564997b..f98b799 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ແຕະເພື່ອລຶບຮູບແບບໃບໜ້າຂອງທ່ານ, ຈາກນັ້ນເພີ່ມໃບໜ້າຂອງທ່ານໃສ່ໃໝ່"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍໜ້າ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ປົດລັອກໂທລະສັບຂອງທ່ານໂດຍການເບິ່ງມັນ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ເພື່ອໃຊ້ການປົດລັອກດ້ວຍໜ້າ, ກະລຸນາເປີດໃຊ້ "<b>"ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ"</b>" ໃນການຕັ້ງຄ່າ > ຄວາມເປັນສ່ວນຕົວ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ຕັ້ງຄ່າວິທີເພີ່ມເຕີມເພື່ອປົດລັອກ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ແຕະເພື່ອເພີ່ມລາຍນິ້ວມື"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ປົດລັອກດ້ວຍລາຍນິ້ວມື"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ຕິດຕາມການພະຍາຍາມປົດລັອກໜ້າຈໍ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ຕິດຕາມເບິ່ງຈຳນວນການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງ ໃນເວລາປົດລັອກໜ້າຈໍ ແລະລັອກແທັບເລັດ ຫຼືລຶບຂໍ້ມູນທັງໝົດຂອງແທັບເລັດ ຖ້າມີການພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງຫຼາຍເທື່ອເກີນໄປ."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ຕິດຕາມຈຳນວນລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງທີ່ພິມຕອນກຳລັງປົດລັອກໜ້າຈໍ ແລະ ລັອກອຸປະກອນ Android TV ຂອງທ່ານ ຫຼື ລຶບຂໍ້ມູນຂອງອຸປະກອນ Android TV ຂອງທ່ານຫາກພິມລະຫັດຜ່ານຜິດຫຼາຍເທື່ອເກີນໄປ."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ຕິດຕາມຈຳນວນການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງໃນເວລາປົດລັອກໜ້າຈໍ ແລະ ລັອກລະບົບສາລະບັນເທີງ ຫຼື ລຶບຂໍ້ມູນຂອງລະບົບສາລະບັນເທີງທັງໝົດຫາກມີການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງຫຼາຍເກີນໄປ."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ຕິດຕາມເບິ່ງຈຳນວນການພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ ໃນເວລາປົດລັອກໜ້າຈໍ ແລະລັອກໂທລະສັບ ຫຼືລຶບຂໍ້ມູນທັງໝົດຂອງໂປລະສັບ ຖ້າມີການພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງຫຼາຍເທື່ອເກີນໄປ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ຕິດຕາມຈຳນວນຂອງລະຫັດຜ່ານບໍ່ຖືກຕ້ອງທີ່ພິມໄປແລ້ວ ເມື່ອປົດລັອກໜ້າຈໍ, ແລະລັອກແທັບເລັດ ຫຼືລຶບທຸກຂໍ້ມູນຜູ້ໃຊ້ນີ້ ຖ້າໄດ້ພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງເຂົ້າໄປຫຼາຍອັນເກີນໄປ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ຕິດຕາມຈຳນວນຂອງລະຫັດຜ່ານບໍ່ຖືກຕ້ອງທີ່ພິມໄປແລ້ວຕອນກຳລັງປົດລັອກໜ້າຈໍ ແລະ ລັອກອຸປະກອນ Android TV ຂອງທ່ານ ຫຼື ລຶບທຸກຂໍ້ມູນຜູ້ໃຊ້ນີ້ຖ້າພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງເຂົ້າໄປຫຼາຍເທື່ອເກີນໄປ."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ຕິດຕາມຈຳນວນການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງໃນເວລາປົດລັອກໜ້າຈໍ ແລະ ລັອກລະບົບສາລະບັນເທີງ ຫຼື ລຶບຂໍ້ມູນຂອງໂປຣໄຟລ໌ນີ້ທັງໝົດຫາກມີການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງຫຼາຍເກີນໄປ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ຕິດຕາມຈຳນວນຂອງລະຫັດຜ່ານບໍ່ຖືກຕ້ອງທີ່ພິມໄປແລ້ວ ເມື່ອປົດລັອກໜ້າຈໍ, ແລະລັອກໂທລະສັບ ຫຼືລຶບທຸກຂໍ້ມູນຜູ້ໃຊ້ນີ້ ຖ້າໄດ້ພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງເຂົ້າໄປຫຼາຍອັນເກີນໄປ."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ປ່ຽນລັອກໜ້າຈໍ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ປ່ຽນລັອກໜ້າຈໍ."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ລຶບຂໍ້ມູນທັງໝົດ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ລຶບຂໍ້ມູນຂອງແທັບເລັດໂດຍບໍ່ມີການເຕືອນ ໂດຍການຣີເຊັດກັບຄືນໃຫ້ເປັນແບບທີ່ມາຈາກໂຮງງານ."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ລຶບຂໍ້ມູນຂອງອຸປະກອນ Android TV ທ່ານໂດຍບໍ່ຕ້ອງແຈ້ງເຕືອນດ້ວຍການຣີເຊັດຂໍ້ມູນເປັນຄ່າເລີ່ມຕົ້ນຈາກໂຮງງານ."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ລຶບຂໍ້ມູນຂອງລະບົບສາລະບັນເທີງໂດຍບໍ່ມີຄຳເຕືອນດ້ວຍການດຳເນີນການຕັ້ງຄ່າຈາກໂຮງງານ."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ລຶບຂໍ້ມູນຂອງໂທລະສັບໂດຍບໍ່ມີການເຕືອນ ໂດຍການຣີເຊັດກັບຄືນໃຫ້ເປັນແບບທີ່ມາຈາກໂຮງງານ."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ລຶບຂໍ້ມູນຜູ້ໃຊ້"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ລຶບຂໍ້ມູນໂປຣໄຟລ໌"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ລຶບຂໍ້ມູນຜູ້ໃຊ້"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ລຶບຂໍ້ມູນຂອງຜູ້ໃຊ້ນີ້ຢູ່ໃນໂທລະທັດນີ້ໂດຍບໍ່ມີການເຕືອນ."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ລຶບຂໍ້ມູນຂອງຜູ້ໃຊ້ນີ້ຢູ່ອຸປະກອນ Android TV ນີ້ໂດຍບໍ່ຕ້ອງມີການເຕືອນ."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ລຶບຂໍ້ມູນຂອງໂປຣໄຟລ໌ນີ້ຢູ່ລະບົບສາລະບັນເທີງນີ້ໂດຍບໍ່ມີຄຳເຕືອນ."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ລຶບຂໍ້ມູນຂອງຜູ້ໃຊ້ນີ້ຢູ່ໃນໂທລະສັບນີ້ໂດຍບໍ່ມີການເຕືອນ."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ປ່ຽນ proxy ຮວມຂອງອຸປະກອນ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ຕັ້ງໃຫ້ພຣັອກຊີສ່ວນກາງຂອງອຸປະກອນ ທີ່ຈະໃຊ້ໃນຂະນະທີ່ເປີດນຳໃຊ້ນະໂຍບາຍ. ພຽງແຕ່ເຈົ້າຂອງອຸປະກອນເທົ່ານັ້ນສາມາດຕັ້ງພຣັອກຊີທົ່ວໄປໄດ້."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index d09866c..88d005d 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Palieskite, kad ištrintumėte veido modelį, tada iš naujo pridėkite veidą"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Atrakinimo pagal veidą nustatymas"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Atrakinkite telefoną pažiūrėję į jį"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Jei norite naudoti atrakinimą pagal veidą, įjunkite parinktį "<b>"Prieiga prie fotoaparato"</b>" skiltyje „Nustatymai“ > „Privatumas“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Daugiau atrakinimo metodų nustatymas"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Palieskite, kad pridėtumėte kontrolinį kodą"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Atrakinimas kontroliniu kodu"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Stebėti bandymus atrakinti ekraną"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Stebimas neteisingai įvestų slaptažodžių skaičius atrakinant ekraną ir užrakinti planšetinį kompiuterį arba ištrinti visus jame esančius duomenis, jei įvedama per daug neteisingų slaptažodžių."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Stebėkite atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinkite „Android TV“ įrenginį arba ištrinkite visus „Android TV“ įrenginio duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Stebėti atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinti informacinę pramoginę sistemą arba ištrinti visus informacinės pramoginės sistemos duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Atrakindami ekraną stebėkite neteisingai įvestų slaptažodžių skaičių ir užrakinkite telefoną ar ištrinkite visus telefono duomenis, jei įvedama per daug neteisingų slaptažodžių."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Stebėkite atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinkite planšetinį kompiuterį arba ištrinkite visus šio naudotojo duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Stebėkite atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinkite „Android TV“ įrenginį arba ištrinkite visus šio naudotojo duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Stebėti atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinti informacinę pramoginę sistemą arba ištrinti visus šio profilio duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Stebėkite atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinkite telefoną arba ištrinkite visus šio naudotojo duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Pakeisti ekrano užraktą"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Pakeisti ekrano užraktą."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Trinti visus duomenis"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Be įspėjimo ištrinti planšetinio kompiuterio duomenis atkuriant gamyklinius duomenis."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Neįspėjus ištrinami „Android TV“ įrenginio duomenys, atkūrus gamyklinius duomenis."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ištrinti informacinės pramoginės sistemos be įspėjimo atliekant gamyklinių duomenų atkūrimą."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Be įspėjimo ištrinti telefono duomenis atkuriant gamyklinius duomenis."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Naudotojo duomenų ištrynimas"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profilio duomenų ištrynimas"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Naudotojo duomenų ištrynimas"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ištrinkite šio naudotojo duomenis šiame planšetiniame kompiuteryje be įspėjimo."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ištrinami šio naudotojo duomenys šiame „Android TV“ įrenginyje be įspėjimo."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ištrinti šio profilio duomenis šioje informacinėje pramoginėje sistemoje be įspėjimo."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ištrinkite šio naudotojo duomenis šiame telefone be įspėjimo."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Nustatyti įrenginio bendrąjį tarpinį serverį"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Nustatykite įrenginio visuotinį tarpinį serverį, kuris bus naudojamas, kai politika įgalinta. Tik įrenginio savininkas gali nustatyti visuotinį tarpinį serverį."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a2aa0ce..50d948a 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -221,7 +221,7 @@
<string name="turn_on_radio" msgid="2961717788170634233">"Ieslēgt bezvadu tīklu"</string>
<string name="turn_off_radio" msgid="7222573978109933360">"Izslēgt bezvadu tīklu"</string>
<string name="screen_lock" msgid="2072642720826409809">"Bloķēt ekrānu"</string>
- <string name="power_off" msgid="4111692782492232778">"Strāvas padeve ir izslēgta."</string>
+ <string name="power_off" msgid="4111692782492232778">"Izslēgt tālruni"</string>
<string name="silent_mode_silent" msgid="5079789070221150912">"Zvanītājs izslēgts"</string>
<string name="silent_mode_vibrate" msgid="8821830448369552678">"Zvanītājs vibrācijas režīmā"</string>
<string name="silent_mode_ring" msgid="6039011004781526678">"Zvanītājs ieslēgts"</string>
@@ -245,7 +245,7 @@
<string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV opcijas"</string>
<string name="global_actions" product="default" msgid="6410072189971495460">"Tālruņa opcijas"</string>
<string name="global_action_lock" msgid="6949357274257655383">"Ekrāna bloķētājs"</string>
- <string name="global_action_power_off" msgid="4404936470711393203">"Strāvas padeve izslēgta."</string>
+ <string name="global_action_power_off" msgid="4404936470711393203">"Izslēgt strāvas padevi"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"Barošana"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Restartēt"</string>
<string name="global_action_emergency" msgid="1387617624177105088">"Ārkārtas situācija"</string>
@@ -619,6 +619,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Pieskarieties, lai izdzēstu sejas modeli, un pēc tam vēlreiz pievienojiet seju"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Autorizācijas pēc sejas iestatīšana"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Atbloķējiet tālruni, skatoties uz to"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Lai izmantotu autorizāciju pēc sejas, sadaļā Iestatījumi > Konfidencialitāte ieslēdziet opciju "<b>"Piekļuve kamerai"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Citi atbloķēšanas veidi"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Pieskarieties, lai pievienotu pirksta nospiedumu"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Autorizācija ar pirksta nospiedumu"</string>
@@ -732,9 +733,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Pārrauga nepareizi ievadīto paroļu skaitu, atbloķējot ekrānu, un bloķē planšetdatoru vai dzēš visus planšetdatora datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt Android TV vai dzēst visus Android TV ierīces datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Ekrāna atbloķēšanas laikā pārraudzīt nepareizi ievadīto paroļu skaitu un bloķēt informatīvi izklaidējošo sistēmu vai dzēst visus informatīvi izklaidējošās sistēmas datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Pārrauga nepareizi ievadīto paroļu skaitu, atbloķējot ekrānu, un bloķē tālruni vai dzēš visus tālruņa datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt planšetdatoru vai dzēst visus šī lietotāja datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt Android TV ierīci vai dzēst visus šī lietotāja datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt informatīvi izklaidējošo sistēmu vai dzēst visus šī profila datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt tālruni vai dzēst visus šī lietotāja datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Mainīt ekrāna bloķēšanas iestatījumus"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Maina ekrāna bloķēšanas iestatījumu."</string>
@@ -743,10 +746,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Dzēst visus datus"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Dzēš planšetdatora datus bez brīdinājuma, veicot rūpnīcas datu atiestatīšanu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Dzēst Android TV ierīces datus bez brīdinājuma, veicot rūpnīcas datu atiestatīšanu."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Bez brīdinājuma dzēst informatīvi izklaidējošās sistēmas datus, veicot rūpnīcas datu atiestatīšanu."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Dzēš tālruņa datus bez brīdinājuma, veicot rūpnīcas datu atiestatīšanu."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Dzēst lietotāja datus"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profila datu dzēšana"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Dzēst lietotāja datus"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Bez brīdinājuma dzēst šī lietotāja datus no planšetdatora."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Bez brīdinājuma dzēst šī lietotāja datus no Android TV ierīces."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bez brīdinājuma dzēst šī profila datus no šīs informatīvi izklaidējošās sistēmas."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Bez brīdinājuma dzēst šī lietotāja datus no tālruņa."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Iestatīt ierīces globālo starpniekserveri"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Iestatīt ierīces globālo starpniekserveri, kas jāizmanto, kad politika ir iespējota. Globālo starpniekserveri var iestatīt tikai ierīces īpašnieks."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 9d46ecc..3602d2d 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Допрете за да го избришете вашиот модел на лице, а потоа повторно додајте го лицето"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Поставете „Отклучување со лик“"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Отклучете го телефонот со гледање во него"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"За да користите „Отклучување со лик“, вклучете "<b>"Пристап до камерата"</b>" во „Поставки > Приватност“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Поставете уште начини за отклучување"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Допрете за да додадете отпечаток"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Отклучување со отпечаток на прст"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Следи ги обидите за отклучување на екранот"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Посматрај го бројот на неточни лозинки што се напишани за да се отклучи екранот и заклучи го таблетот или избриши ги сите податоци од него ако бидат напишани премногу неточни лозинки."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Го следи бројот на погрешно внесени лозинки при отклучување на екранот и го заклучува уредот Android TV или ги брише сите податоци од уредот Android TV доколку се внесени премногу погрешни лозинки."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го системот за информации и забава или избриши ги сите негови податоци доколку се внесени премногу погрешни лозинки."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Посматрај го бројот на неточни лозинки што се напишани за да се отклучи екранот и заклучи го телефонот или избриши ги сите податоци од него ако бидат напишани премногу неточни лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го таблетот или избриши ги сите податоци од овој корисник доколку се внесени премногу погрешни лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Го следи бројот на погрешно внесени лозинки при отклучување на екранот и го заклучува уредот Android TV или ги брише сите податоци од овој корисник доколку се внесени премногу погрешни лозинки."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го системот за информации и забава или избриши ги сите податоци од овој профил доколку се внесени премногу погрешни лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го телефонот или избриши ги сите податоци од овој корисник доколку се внесени премногу погрешни лозинки."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Промени го заклучувањето на екранот"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Промени го заклучувањето на екранот."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Избриши ги сите податоци"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Избриши ги податоците во таблетот без предупредување со ресетирање на фабрички податоци."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ги брише податоците на вашиот уред Android TV без предупредување, така што ќе изврши ресетирање на фабричките податоци."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Избриши ги податоците во системот за информации и забава без предупредување со ресетирање на фабрички податоци."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Избриши ги податоците во телефонот без предупредување со ресетирање на фабрички податоци."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Избриши ги податоците на корисникот"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Избриши ги податоците на профилот"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Избриши ги податоците на корисникот"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Избриши ги податоците на овој корисник на таблетот без предупредување."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ги брише податоците на овој корисник на уредов Android TV без предупредување."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Избриши ги податоците на профилов во системот за информации и забава без предупредување."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Избриши ги податоците на овој корисник на телефонот без предупредување."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Постави го глобалниот прокси на уредот"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Поставете го глобалниот прокси на уредот да се користи додека политиката е овозможена. Само сопственикот на уредот може да го поставува глобалниот прокси."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index a5e2cbc..1aa9b61 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"നിങ്ങളുടെ മുഖ മോഡൽ ഇല്ലാതാക്കാൻ ടാപ്പ് ചെയ്യുക, തുടർന്ന് അത് വീണ്ടും ചേർക്കുക"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ഫെയ്സ് അൺലോക്ക് സജ്ജീകരിക്കുക"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ഫോണിലേക്ക് നോക്കി അത് അൺലോക്ക് ചെയ്യുക"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ഫെയ്സ് അൺലോക്ക് ഉപയോഗിക്കാൻ, ക്രമീകരണം > സ്വകാര്യത എന്നതിൽ "<b>"ക്യാമറാ ആക്സസ്"</b>" ഓണാക്കുക"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"അൺലോക്ക് ചെയ്യുന്നതിനുള്ള കൂടുതൽ വഴികൾ സജ്ജീകരിക്കുക"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ഫിംഗർപ്രിന്റ് ചേർക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ഫിംഗർപ്രിന്റ് അൺലോക്ക്"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"സ്ക്രീൻ അൺലോക്ക് ശ്രമങ്ങൾ നിരീക്ഷിക്കുക"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വ്ഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ടാബ്ലെറ്റ് ലോക്കുചെയ്യുകയോ ടാബ്ലെറ്റിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android TV ലോക്ക് ചെയ്യുകയോ Android TV-യിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക. നിരവധി തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്താൽ, ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റം ലോക്ക് ചെയ്യുകയോ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റത്തിന്റെ ഡാറ്റ മുഴുവനും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വ്ഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ഫോൺ ലോക്കുചെയ്യുകയോ ഫോണിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ പാസ്വേഡ് ടൈപ്പുചെയ്തെങ്കിൽ ടാബ്ലെറ്റ് ലോക്കുചെയ്യുകയോ ഈ എല്ലാ ഉപയോക്തൃവിവരവും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android TV ലോക്ക് ചെയ്യുകയോ ഈ ഉപയോക്തൃ ഡാറ്റയെല്ലാം മായ്ക്കുകയോ ചെയ്യുക."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിച്ച്, നിരവധി തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്താൽ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റം ലോക്ക് ചെയ്യുകയോ ഈ പ്രൊഫൈലിന്റെ ഡാറ്റ മുഴുവനും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ പാസ്വേഡ് ടൈപ്പുചെയ്തെങ്കിൽ ഫോൺ ലോക്കുചെയ്യുകയോ ഈ എല്ലാ ഉപയോക്തൃവിവരവും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"സ്ക്രീൻ ലോക്ക് മാറ്റുക"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"സ്ക്രീൻ ലോക്ക് മാറ്റുക."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"എല്ലാ ഡാറ്റയും മായ്ക്കുക"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ഒരു ഫാക്ടറി ഡാറ്റ പുനഃസജ്ജീകരണം നടപ്പിലാക്കുന്നതിലൂടെ ടാബ്ലെറ്റിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ഫാക്ടറി ഡാറ്റ റീസെറ്റ് ചെയ്ത് നിങ്ങളുടെ Android TV-യിലെ ഉപകരണ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ഒരു ഫാക്ടറി ഡാറ്റാ റീസെറ്റിലൂടെ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റത്തിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ഒരു ഫാക്ടറി ഡാറ്റാ റീസെറ്റിലൂടെ ഫോണിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ഉപയോക്തൃ ഡാറ്റ മായ്ക്കുക"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"പ്രൊഫൈൽ ഡാറ്റ മായ്ക്കുക"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ഉപയോക്തൃ ഡാറ്റ മായ്ക്കുക"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"മുന്നറിയിപ്പൊന്നും നൽകാതെ ഈ ടാബ്ലെറ്റിലെ ഈ ഉപയോക്താവിന്റെ ഡാറ്റ മായ്ക്കുക."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ഈ Android TV-യിലെ ഈ ഉപയോക്തൃ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ഈ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റത്തിലെ ഈ പ്രൊഫൈലിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"മുന്നറിയിപ്പൊന്നും നൽകാതെ ഈ ഫോണിലെ ഈ ഉപയോക്താവിന്റെ ഡാറ്റ മായ്ക്കുക."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ഉപകരണ ഗ്ലോബൽ പ്രോക്സി സജ്ജീകരിക്കുക"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"നയം പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുമ്പോൾ ഉപകരണ ഗ്ലോബൽ പ്രോക്സി ഉപയോഗിക്കുന്നത് സജ്ജമാക്കുക. ഉപകരണ ഉടമയ്ക്ക് മാത്രമേ ഗ്ലോബൽ പ്രോക്സി സജ്ജമാക്കാനാകൂ."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 47c8560..6227b81 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Нүүрний загвараа устгахын тулд товшоод, дараа нь царайгаа дахин нэмнэ үү"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Царайгаар түгжээ тайлахыг тохируулах"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Утас руугаа харж түгжээг нь тайлна уу"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Царайгаар түгжээ тайлахыг ашиглахын тулд Тохиргоо > Нууцлал хэсэгт "<b>" Камерын хандалтыг "</b>" асаана уу"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Түгжээ тайлах илүү олон арга тохируулна уу"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Хурууны хээ нэмэхийн тулд товшино уу"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Хурууны хээгээр түгжээ тайлах"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Дэлгэцийн түгжээг тайлах оролдлогыг хянах"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Дэлгэц түгжигдсэн үед нууц үг буруу оруулалтын тоог хянах ба хэрэв хэт олон удаа нууц үгийг буруу оруулбал таблетыг түгжих болон таблетын бүх датаг арилгана"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Дэлгэцийн түгжээг тайлахаар буруу оруулсан нууц үгийн тоог хянаж, нууц үгийг хэт олон удаа буруу оруулсан тохиолдолд таны Android TV төхөөрөмжийг түгжиж эсвэл үүний бүх өгөгдлийг устгана."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Дэлгэцийн түгжээг тайлахад буруу бичиж оруулсан нууц үгний тоог хянаж, инфотэйнмент системийг түгжих эсвэл хэт олон удаа нууц үгийг буруу бичиж оруулсан тохиолдолд инфотэйнмент системийн бүх өгөгдлийг устгана."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Дэлгэц түгжигдсэн үед нууц үг буруу оруулалтын тоог хянах, ба хэрэв хэт олон удаа нууц үгийг буруу оруулбал утсыг түгжих болон утасны бүх датаг арилгана"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Дэлгэцийн түгжээг тайлахад оруулсан буруу нууц үгийн давтамжийг хянаж таблетыг түгжих эсвэл буруу нууц үгийг хэт олон удаа оруулсан тохиолдолд энэ хэрэглэгчийн мэдээллийг устгах."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Дэлгэцийн түгжээг тайлахаар буруу оруулсан нууц үгийн тоог хянаж, нууц үгийг хэт олон удаа буруу оруулсан тохиолдолд таны Android TV төхөөрөмжийг түгжиж эсвэл энэ хэрэглэгчийн бүх өгөгдлийг устгана."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Дэлгэцийн түгжээг тайлахад буруу бичиж оруулсан нууц үгний тоог хянаж, инфотэйнмент системийг түгжих эсвэл хэт олон удаа нууц үгийг буруу бичиж оруулсан тохиолдолд энэ профайлын бүх өгөгдлийг устгана."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Дэлгэцийн түгжээг тайлахад оруулсан буруу нууц үгийн давтамжийг хянаж гар утсыг түгжих эсвэл буруу нууц үгийг хэт олон удаа оруулсан тохиолдолд энэ хэрэглэгчийн мэдээллийг устгах."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Дэлгэцийн түгжээг өөрчлөх"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Дэлгэцийн түгжээг өөрчлөх."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Бүх датаг арилгах"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Үйлдвэрийн дата утгыг өгсөнөөр таблетын дата шууд арилгагдана."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Таны Android TV төхөөрөмжийн өгөгдлийг танд анхааруулалгүйгээр үйлдвэрээс гарсан төлөвт шилжүүлэн устгана."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Үйлдвэрийн өгөгдлийн төлөвт үйлдлийг гүйцэтгэснээр инфотэйнмент системийн өгөгдлийг сануулгагүйгээр устгана."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Сануулахгүйгээр утасны бүх мэдээллийг устгаж, үйлдвэрийн өгөгдмөл байдалд шилжүүлнэ"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Хэрэглэгчийн мэдээллийг арилгах"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Профайлын өгөгдлийг устгах"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Хэрэглэгчийн мэдээллийг арилгах"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Анхааруулга өгөхгүйгээр энэ хэрэглэгчийн энэ таблет дээрх мэдээллийг устгах."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Энэ Android TV төхөөрөмж дээрх хэрэглэгчийн өгөгдлийг хэрэглэгчид анхааруулалгүйгээр устгана."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Энэ инфотэйнмент систем дээр энэ профайлын өгөгдлийг сануулгагүйгээр устгана."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Анхааруулга өгөхгүйгээр энэ хэрэглэгчийн энэ гар утсан дээрх мэдээллийг устгах."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Төхөрөөмжийн глобал проксиг тохируулах"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Бодлогыг ашиглах боломжтой үед төхөөрөмжийн олон улсын эрхийг тохируулах. Зөвхөн төхөөрөмж эзэмшигч нь олон улсын эрхийг тохируулах боломжтой."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 8d4956d..a080d810 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -548,7 +548,7 @@
<string name="permlab_disableKeyguard" msgid="3605253559020928505">"तुमचे स्क्रीन लॉक अक्षम करा"</string>
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"स्क्रीन लॉक क्लिष्टतेची विनंती करा"</string>
- <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"अॅपला स्क्रीन लॉक क्लिष्टता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य रेंज आणि स्क्रीन लॉकचा प्रकार सूचित करते. अॅप वापरकर्त्यांना असेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठराविक पातळीपर्यंत अपडेट करावे, परंतु वापरकर्ते त्याकडे मोकळेपणाने दुर्लक्ष करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string>
+ <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"अॅपला स्क्रीन लॉक क्लिष्टता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य रेंज आणि स्क्रीन लॉकचा प्रकार सूचित करते. अॅप वापरकर्त्यांना असेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठरावीक पातळीपर्यंत अपडेट करावे, परंतु वापरकर्ते त्याकडे मोकळेपणाने दुर्लक्ष करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"बायोमेट्रिक हार्डवेअर वापरा"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ऑथेंटिकेशनसाठी बायोमेट्रिक हार्डवेअरचा वापर करण्याची ॲपला अनुमती देते"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"फिंगरप्रिंट हार्डवेअर व्यवस्थापित करा"</string>
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"फेस मॉडेल हटवण्यासाठी टॅप करा, त्यानंतर तुमचा चेहरा पुन्हा जोडा"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलॉक सेट करा"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"तुमच्या फोनकडे पाहून तो अनलॉक करा"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फेस अनलॉक वापरण्यासाठी, सेटिंग्ज > गोपनीयता येथे "<b>"कॅमेरा अॅक्सेस"</b>" सुरू करा"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलॉक करण्याच्या आणखी पद्धती सेट करा"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फिंगरप्रिंट जोडण्यासाठी टॅप करा"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फिंगरप्रिंट अनलॉक"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रीन अनलॉक प्रयत्नांचे परीक्षण करा"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"टाइप केलेल्या अयोग्य पासवर्डांच्या अंकांचे परीक्षण करा. स्क्रीन अनलॉक केली जाते, तेव्हा टॅबलेट लॉक करा किंवा बरेच पासवर्ड टाइप केले असल्यास टॅबलेटचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रीन अनलॉक करताना टाइप केलेल्या चुकीच्या पासवर्ड संख्येचे परीक्षण करते आणि Android TV डिव्हाइस लॉक करते किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास Android TV डिव्हाइसचा सर्व डेटा मिटवते."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"टाइप केलेल्या चुकीच्या पासवर्डच्या संख्येचे निरीक्षण करा. स्क्रीन अनलॉक करताना, इंफोटेनमेंट सिस्टीम लॉक करा किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास, इंफोटेनमेंट सिस्टीमचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"टाइप केलेल्या अयोग्य पासवर्डांच्या अंकांचे परीक्षण करा. स्क्रीन अनलॉक केली जाते, तेव्हा फोन लॉक करा किंवा बरेच पासवर्ड टाइप केले असल्यास फोनचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रीन अनलॉक करताना टाइप केलेल्या चुकीच्या पासवर्डांच्या संख्येचे परीक्षण करा आणि टॅबलेट लॉक करा किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास या वापरकर्त्याचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रीन अनलॉक करताना टाइप केलेल्या चुकीच्या पासवर्ड संख्येचे परीक्षण करते आणि Android TV डिव्हाइस लॉक करते किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास वापरकर्त्याचा सर्व डेटा मिटवते."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रीन अनलॉक करताना टाइप केलेल्या चुकीच्या पासवर्डच्या संख्येचे निरीक्षण करा आणि इंफोटेनमेंट सिस्टीम लॉक करा किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास, या प्रोफाइलचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"टाइप केलेल्या अयोग्य पासवर्डांच्या अंकांचे परीक्षण करा. स्क्रीन अनलॉक केली जाते, तेव्हा फोन लॉक करा किंवा बरेच पासवर्ड टाइप केले असल्यास या वापरकर्त्याचा सर्व डेटा मिटवा."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"स्क्रीन लॉक बदला"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"स्क्रीन लॉक बदला."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"सर्व डेटा मिटवा"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"फॅक्टरी डेटा रीसेट करून चेतावणीशिवाय टॅब्लेटचा डेटा मिटवा."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"चेतावणी न देता फॅक्टरी डेटा रीसेट करून Android TV डिव्हाइसचा डेटा मिटवा."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"फॅक्टरी डेटा रीसेट करून कोणत्याही चेतावणीशिवाय इंफोटेनमेंट सिस्टीमचा डेटा मिटवा."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"फॅक्टरी डेटा रीसेट करून चेतावणीशिवाय फोनचा डेटा मिटवा."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"वापरकर्ता डेटा मिटवा"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफाइल डेटा मिटवा"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"वापरकर्ता डेटा मिटवा"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"कोणत्याही चेतावणी शिवाय या वापरकर्त्याचा या टॅब्लेटवरील डेटा मिटवा."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"कोणत्याही चेतावणीशिवाय या Android TV डिव्हाइसवरील वापरकर्त्याचा डेटा मिटवा."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"कोणत्याही चेतावणीशिवाय या इंफोटेनमेंट सिस्टीमवरील प्रोफाइलचा डेटा मिटवा."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"कोणत्याही चेतावणी शिवाय या वापरकर्त्याचा या फोनवरील डेटा मिटवा."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"डिव्हाइस समग्र प्रॉक्सी सेट करा"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"धोरण सक्षम असताना वापरण्यासाठी डिव्हाइस समग्र प्रॉक्सी सेट करा. फक्त डिव्हाइस मालक समग्र प्रॉक्सी सेट करु शकतो."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 4daa051..c13c9d6 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketik untuk memadamkan model wajah anda, kemudian tambahkan wajah anda semula"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Sediakan Buka Kunci Wajah"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci telefon anda dengan melihat telefon anda"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Buka Kunci Wajah, hidupkan "<b>"akses Kamera"</b>" dalam Tetapan > Privasi"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Sediakan lebih banyak cara untuk membuka kunci"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketik untuk menambahkan cap jari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Buka Kunci Cap Jari"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau percubaan buka kunci skrin"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Memantau bilangan kata laluan yang tersilap ditaip apabila membuka skrin, dan mengunci tablet atau memadam semua data tablet jika terlalu banyak kesilapan menaip kata laluan."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Pantau bilangan kata laluan salah yang ditaip semasa membuka kunci skrin, dan kunci peranti Android TV anda atau padamkan semua data peranti Android TV jika terlalu banyak kata laluan yang salah ditaip."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Memantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan mengunci sistem maklumat hibur atau memadam semua data sistem maklumat hibur jika terlalu banyak kata laluan yang tidak betul ditaip."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Memantau bilangan kata laluan salah yang ditaip semasa membuka skrin, dan mengunci telefon atau memadam semua data telefon jika terlalu banyak kata laluan salah ditaip."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Pantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan kunci tablet atau padam semua data pengguna ini jika terlalu banyak kata laluan yang tidak betul ditaip."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Pantau bilangan kata laluan salah yang ditaip semasa membuka kunci skrin, dan kunci peranti Android TV anda atau padamkan semua data pengguna ini jika terlalu banyak kata laluan yang salah ditaip."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Memantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan mengunci sistem maklumat hibur atau memadam semua data profil ini jika terlalu banyak kata laluan yang tidak betul ditaip."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Pantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan kunci telefon atau padam semua data pengguna ini jika terlalu banyak kata laluan yang tidak betul ditaip."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Tukar kunci skrin"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Tukar kunci skrin."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Padamkan semua data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Memadamkan data tablet tanpa amaran dengan melakukan tetapan semula data kilang."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Padamkan data peranti Android TV anda tanpa amaran dengan melaksanakan tetapan semula data kilang."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Memadam data sistem maklumat hibur tanpa amaran dengan melakukan tetapan semula data kilang."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Padamkan data telefon tanpa amaran dengan melakukan tetapan semula data kilang."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Padam data pengguna"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Padam data profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Padam data pengguna"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Padam data pengguna ini pada tablet ini tanpa amaran."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Padamkan data pengguna ini pada peranti Android TV tanpa amaran."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Memadam data profil pada sistem maklumat hibur ini tanpa amaran."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Padam data pengguna ini pada telefon ini tanpa amaran."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Tetapkan proksi global peranti"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Tetapkan proksi global peranti untuk digunakan sementara dasar didayakan. Hanya pemilik peranti boleh menetapkan proksi global."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 886f1ee..aeda707 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"သင်၏မျက်နှာနမူနာကို ဖျက်ရန် တို့ပါ။ ထို့နောက် သင့်မျက်နှာကို ထပ်ထည့်ပါ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို ထည့်သွင်းပါ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"သင့်ဖုန်းကိုကြည့်၍ သော့ဖွင့်ပါ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို သုံးရန် "<b>"ကင်မရာ သုံးခွင့်"</b>" ကို ‘ဆက်တင်များ > ကန့်သတ်ဆက်တင်’ တွင်ဖွင့်ပါ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"သော့ဖွင့်ရန် နောက်ထပ်နည်းလမ်းများကို စနစ်ထည့်သွင်းပါ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"လက်ဗွေထည့်ရန် တို့ပါ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"မျက်နှာပြင်လော့ခ်ဖွင့်ရန် ကြိုးပမ်းမှုများကို စောင့်ကြည့်ပါ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"မျက်နှာပြင်ကို သော့ဖွင့်ရန် အတွက် စကားဝှက် မမှန်မကန် ထည့်သွင်းမှု အရေအတွက်ကို စောင့်ကြည့်လျက်၊ စကားဝှက် ရိုက်ထည့်မှု သိပ်များနေလျှင် တက်ဘလက်ကို သော့ခတ်ရန် သို့မဟုတ် တက်ဘလက် ဒေတာ အားလုံးကို ဖျက်ရန်။"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"မျက်နှာပြင်ကို လော့ခ်ဖွင့်သည့်အခါ စကားဝှက်မှားယွင်းစွာ ရိုက်သွင်းသည့်အကြိမ်ရေကို စောင့်ကြည့်ပြီး မှားယွင်းသည့်အကြိမ်ရေ အလွန်များလာပါက သင့် Android TV စက်ပစ္စည်းကို လော့ခ်ချခြင်း သို့မဟုတ် သင့် Android TV ရှိ အသုံးပြုသူဒေတာများအားလုံးကို ဖျက်ခြင်းတို့ ပြုလုပ်သွားပါမည်။"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ဖန်သားပြင်လော့ခ်ဖွင့်ရန် အတွက် စကားဝှက် မမှန်မကန် ထည့်သွင်းမှု အရေအတွက်ကို စောင့်ကြည့်လျက် စကားဝှက် မမှန်မကန် ရိုက်ထည့်မှု များနေလျှင် သတင်းနှင့်ဖျော်ဖြေရေး စနစ်ကို လော့ခ်ချသည် (သို့) ၎င်း၏ ဒေတာအားလုံးကို ဖျက်သည်။"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"မျက်နှာပြင်ကို သော့ဖွင့်ရန် အတွက် စကားဝှက် မမှန်မကန် ထည့်သွင်းမှု အရေအတွက်ကို စောင့်ကြည့်လျက်၊ စကားဝှက် ရိုက်ထည့်မှု သိပ်များနေလျှင် ဖုန်းကို သော့ခတ်ရန် သို့မဟုတ် ဖုန်း ဒေတာ အားလုံးကို ဖျက်ရန်။"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ဖန်မျက်နှာပြင်အား သော့ဖွင့်စဉ် လျှို့ဝှက်ကုဒ်အမှားများ ရိုက်သွင်းမှုအား စောင့်ကြည့်ရန်နှင့်၊ လျှို့ဝှက်ကုဒ်အမှားများ များစွာ ရိုက်သွင်းပါက တက်ဘလက်အား သော့ချခြင်း သို့မဟုတ် တက်ဘလက်၏ အချက်အလက်များအား ဖျက်ပစ်ခြင်းများ ပြုလုပ်မည်။"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"မျက်နှာပြင်ကို လော့ခ်ဖွင့်သည့်အခါ စကားဝှက်မှားယွင်းစွာ ရိုက်သွင်းသည့်အကြိမ်ရေကို စောင့်ကြည့်ပြီး မှားယွင်းသည့်အကြိမ်ရေ အလွန်များလာပါက သင့် Android TV စက်ပစ္စည်းကို လော့ခ်ချခြင်း သို့မဟုတ် ဤအသုံးပြုသူဒေတာများအားလုံးကို ဖျက်ခြင်းတို့ ပြုလုပ်သွားပါမည်။"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ဖန်သားပြင်လော့ခ်ဖွင့်ရန် အတွက် စကားဝှက် မမှန်မကန် ထည့်သွင်းမှု အရေအတွက်ကို စောင့်ကြည့်လျက် စကားဝှက် မမှန်မကန် ရိုက်ထည့်မှု များနေလျှင် သတင်းနှင့်ဖျော်ဖြေရေး စနစ်ကို လော့ခ်ချသည် (သို့) ဤပရိုဖိုင်၏ ဒေတာအားလုံးကို ဖျက်သည်။"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ဖန်မျက်နှာပြင်အား သော့ဖွင့်စဉ် လျှို့ဝှက်ကုဒ်အမှားများ ရိုက်သွင်းမှုအား စောင့်ကြည့်ရန်နှင့်၊ လျှို့ဝှက်ကုဒ်အမှားများ များစွာ ရိုက်သွင်းပါက ဖုန်းအား သော့ချခြင်း သို့မဟုတ် ဖုန်း၏ အချက်အလက်များအား ဖျက်ပစ်ခြင်းများ ပြုလုပ်မည်။"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ဖန်သားပြင်လော့ခ်ပြောင်းခြင်း"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ဖန်သားပြင်လော့ခ်ပြောင်းသည်။"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ဒေတာအားလုံးအားဖျက်ခြင်း"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"စက်ရုံထုတ် အခြေအနေအား ပြန်ပြောင်းခြင်းဖြင့် တက်ဘလက်ရှိ အချက်အလက်များအား ကြိုတင်သတိပေးမှုမရှိပဲ ဖျက်စီးရန်"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"စက်ရုံထုတ်အခြေအနေ ပြန်ယူရန် လုပ်ဆောင်ခြင်းဖြင့် သင့် Android TV စက်ပစ္စည်းပေါ်ရှိ ဒေတာများကို သတိမပေးဘဲ ဖျက်လိုက်ပါမည်။"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"စက်ရုံထုတ်အခြေအနေပြန်ယူခြင်းကို ဆောင်ရွက်ခြင်းဖြင့် သတင်းနှင့်ဖျော်ဖြေရေး စနစ်၏ ဒေတာကို သတိပေးချက် မပေးဘဲ ဖျက်သည်။"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"စက်ရုံထုတ်အခြေအနေသို့ ပြန်ပြောင်းခြင်းဖြင့် ဖုန်းရှိဒေတာများကို သတိပေးခြင်း မရှိဘဲ ဖျက်သည်။"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"အသုံးပြုသူဒေတာကို ဖျက်မည်"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ပရိုဖိုင်ဒေတာ ဖျက်ခြင်း"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"အသုံးပြုသူဒေတာကို ဖျက်မည်"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"သတိပေးခြင်းမရှိဘဲ ဤတက်ဘလက်ပေါ်ရှိ ထိုအသုံးပြုသူ၏ဒေတာအား ဖျက်မည်။"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ဤ Android TV စက်ပစ္စည်းပေါ်ရှိ အသုံးပြုသူဒေတာများကို သတိပေးခြင်းမရှိဘဲ ဖျက်ပါမည်။"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ဤသတင်းနှင့်ဖျော်ဖြေရေး စနစ်ရှိ ပရိုဖိုင်၏ ဒေတာကို သတိပေးချက် မပေးဘဲ ဖျက်သည်။"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"သတိပေးခြင်းမရှိဘဲ ဤဖုန်းပေါ်ရှိ ထိုအသုံးပြုသူ၏ဒေတာအား ဖျက်မည်။"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"တကမာ္ဘလုံးဆိုင်ရာပရော်စီကို သတ်မှတ်ခြင်း"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ပေါ်လစီအား ဖွင့်ထားချိန်တွင် တစ်ကမ္ဘာလုံးဆိုင်ရာ ပရောက်စီအား အသုံးပြုရန် ကိရိယာကို သတ်မှတ်မည်။ ကိရိယာ၏ ပိုင်ရှင်သာ ကမ္ဘာလုံးဆိုင်ရာ ပရောက်စီကို သတ်မှတ်နိုင်သည်။"</string>
@@ -1159,8 +1165,8 @@
<string name="delete" msgid="1514113991712129054">"ဖျက်ရန်"</string>
<string name="copyUrl" msgid="6229645005987260230">"URLအား ကူးခြင်း"</string>
<string name="selectTextMode" msgid="3225108910999318778">"စာသား ရွေးရန်"</string>
- <string name="undo" msgid="3175318090002654673">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
- <string name="redo" msgid="7231448494008532233">"ထပ်လုပ်ပါ"</string>
+ <string name="undo" msgid="3175318090002654673">"နောက်ပြန်ရန်"</string>
+ <string name="redo" msgid="7231448494008532233">"ပြန်လုပ်ရန်"</string>
<string name="autofill" msgid="511224882647795296">"အော်တိုဖြည့်"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"စာတိုရွေးချယ်မှု"</string>
<string name="addToDictionary" msgid="8041821113480950096">"အဘိဓာန်ထဲ ထည့်ပါ"</string>
@@ -1535,7 +1541,7 @@
<string name="sync_too_many_deletes" msgid="6999440774578705300">"ပယ်ဖျက်မည့်ကန့်သတ်နှုန်းကျော်လွန်သည်"</string>
<string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>၊ account <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> အတွက် စုစုပေါင်း <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> အရာဖျက်ထားပါသည်။ သင်ဘာလုပ်ချင်ပါလဲ?"</string>
<string name="sync_really_delete" msgid="5657871730315579051">"ဤအရာများကိုဖျက်ပါ"</string>
- <string name="sync_undo_deletes" msgid="5786033331266418896">"ဖျက်ပီးသည်များကို ပယ်ဖျက်ရန်"</string>
+ <string name="sync_undo_deletes" msgid="5786033331266418896">"အားလုံးကို မဖျက်တော့ရန်"</string>
<string name="sync_do_nothing" msgid="4528734662446469646">"လက်ရှိ ဘာမှမလုပ်ရန်"</string>
<string name="choose_account_label" msgid="5557833752759831548">"အကောင့် တစ်ခု ရွေးပါ"</string>
<string name="add_account_label" msgid="4067610644298737417">"အကောင့်တစ်ခုထည့်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d873c99..043d506 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Trykk for å slette ansiktsmodellen din, og legg deretter til ansiktet på nytt"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansiktslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås opp telefonen ved å se på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"For å bruke ansiktslås, slå på "<b>"Kameratilgang"</b>" i Innstillinger > Personvern"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måter å låse opp på"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Trykk for å legge til et fingeravtrykk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Opplåsing med fingeravtrykk"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåk forsøk på å låse opp skjermen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Overvåk antall feil passordforsøk ved opplåsing av skjerm, og lås nettbrettet eller slett alle data fra nettbrettet ved for mange feil passordforsøk."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Følg med på hvor mange ganger feil passord skrives inn når skjermen skal låses opp, og lås Android TV-enheten eller tøm alle dataene når feil passord skrives inn for mange ganger."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Overvåk antall feil passord som er skrevet inn ved opplåsing av skjermen, og lås infotainment-systemet eller tøm alle dataene i infotainment-systemet hvis feil passord skrives inn for mange ganger."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Overvåk antall feil passordforsøk ved opplåsing av skjerm, og lås telefonen eller slett alle data fra telefonen ved for mange feil passordforsøk."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser nettbrettet eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Følg med på hvor mange ganger feil passord skrives inn når skjermen skal låses opp, og lås Android TV-enheten eller tøm alle dataene til denne brukeren hvis feil passord skrives inn for mange ganger."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Overvåk antall feil passord som er skrevet inn ved opplåsing av skjermen, og lås infotainment-systemet eller tøm alle dataene i denne profilen hvis feil passord skrives inn for mange ganger."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser telefonen eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Endring av skjermlåsen"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Kan endre skjermlåsen."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Sletting av alle data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Tilbakestill nettbrettets data uten advarsel ved å tilbakestille til fabrikkstandard."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Tøm dataene på Android TV-enheten din uten advarsel ved å tilbakestille til fabrikkstandard."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Tøm dataene i infotainment-systemet uten varsel ved å tilbakestille til fabrikkstandard."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Telefonens data kan slettes uten advarsel ved å tilbakestille til fabrikkstandard."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Slett brukerdataene"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Tøm profildata"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Slett brukerdataene"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Sletter denne brukerens data på dette nettbrettet uten advarsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Tøm dataene til denne brukeren på denne Android TV-enheten uten advarsel."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Tøm dataene i denne profilen på dette infotainment-systemet uten varsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Sletter denne brukerens data på denne telefonen uten advarsel."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Angi enhetens globale proxy-tjener"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Angir den globale proxy-tjeneren på enheten som skal brukes når regelen er aktivert. Bare eieren av enheten kan angi den globale proxy-tjeneren."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index ade87f5..79d55bf 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"आफ्नो फेस मोडेल मेटाउन ट्याप गर्नुहोस् अनि आफ्नो अनुहार फेरि दर्ता गर्नुहोस्"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलक सेटअप गर्नुहोस्"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"फोनमा हेरेकै भरमा फोन अनलक गर्नुहोस्"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फेस अनलक प्रयोग गर्न \"सेटिङ तथा गोपनीयता\" मा गई "<b>"क्यामेरा प्रयोग गर्ने अनुमति"</b>" दिनुहोस्"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलक गर्ने अन्य तरिकाहरू सेटअप गर्नुहोस्"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फिंगरप्रिन्ट हाल्न ट्याप गर्नुहोस्"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फिंगरप्रिन्ट अनलक"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"मनिटरको स्क्रिन अनलक गर्ने प्रयासहरू"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने ट्याब्लेट लक गर्नुहोस् वा ट्याब्लेटका सबै डेटा मेट्नुहोस्।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा डिभाइसमा भएको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गरियोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गरियोस् वा यस इन्फोटेनमेन्ट प्रणालीका सबै डेटा मेटाइयोस्।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रिनअनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने फोन लक गर्नुहोस् वा फोनका सबै डेटा मेट्नुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा ट्याब्लेट लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा यो प्रयोगकर्ताको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गरियोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गरियोस् वा यस प्रोफाइलका सबै डेटा मेटाइयोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा फोन लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"स्क्रिन लक परिवर्तन गर्ने"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"स्क्रिन लक परिवर्तन गर्नुहोस्।"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"सबै डेटा मेट्ने"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नआउँदै ट्याबल्टको डेटा मेट्नुहोस्।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ्याक्ट्री डेटा रिसेट गरेर चेतावनी नदिइकन आफ्नो Android टिभी डिभाइसको डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"यो इन्फोटेनमेन्ट प्रणालीको डेटा कुनै चेतावनीविनै फ्याक्ट्री डेटा रिसेट गरेर मेटाइयोस्।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नदिइकन फोनको डेटा मेट्न।"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"प्रयोगकर्ता डेटा मेट्नुहोस्"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफाइल डेटा मेटाइयोस्"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"प्रयोगकर्ता डेटा मेट्नुहोस्"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"चेतावनी बिना यो ट्याब्लेटमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"यो Android टिभी डिभाइसमा भएको यस प्रयोगकर्ताको डेटा चेतावनी नदिइकन मेटाउनुहोस्।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"यो इन्फोटेनमेन्ट प्रणालीमा भएको यस प्रोफाइलको डेटा कुनै चेतावनीविनै मेटाइयोस्।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"चेतावनी बिना यो फोनमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"उपकरण विश्वव्यापी प्रोक्सी मिलाउनुहोस्"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"नीति सक्षम हुँदा प्रयोग गरिनको लागि यन्त्र ग्लोवल प्रोक्सी सेट गर्नुहोस्। केवल यन्त्र मालिकले ग्लोवल प्रोक्सी सेट गर्न सक्नुहुन्छ।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9203f39..31ae372 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -37,7 +37,7 @@
<string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Kan instellingen voor doorschakelen van gesprekken niet wijzigen vanaf je telefoon tijdens roaming."</string>
<string name="serviceEnabled" msgid="7549025003394765639">"Service staat aan."</string>
<string name="serviceEnabledFor" msgid="1463104778656711613">"Service staat aan voor:"</string>
- <string name="serviceDisabled" msgid="641878791205871379">"Service staat uit."</string>
+ <string name="serviceDisabled" msgid="641878791205871379">"Service is uitgezet."</string>
<string name="serviceRegistered" msgid="3856192211729577482">"De registratie is voltooid."</string>
<string name="serviceErased" msgid="997354043770513494">"Wissen uitgevoerd."</string>
<string name="passwordIncorrect" msgid="917087532676155877">"Onjuist wachtwoord."</string>
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tik om je gezichtsmodel te verwijderen en voeg je gezicht opnieuw toe"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ontgrendelen via gezichtsherkenning instellen"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontgrendel je telefoon door ernaar te kijken"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Als je Ontgrendelen via gezichtsherkenning wilt gebruiken, zet je "<b>"Cameratoegang"</b>" aan via Instellingen > Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer manieren in om te ontgrendelen"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om een vingerafdruk toe te voegen"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ontgrendelen met vingerafdruk"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pogingen voor schermontgrendeling bijhouden"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de tablet vergrendelen of alle gegevens op de tablet wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en het Android TV-apparaat vergrendelen of alle gegevens van het Android TV-apparaat wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Bijhouden hoe vaak onjuiste wachtwoorden worden getypt als het scherm wordt ontgrendeld, en het infotainmentsysteem vergrendelen of alle gegevens op het infotainmentsysteem wissen als te veel onjuiste wachtwoorden worden getypt."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens op de telefoon wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de tablet vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en het Android TV-apparaat vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Bijhouden hoe vaak onjuiste wachtwoorden worden getypt als het scherm wordt ontgrendeld, en het infotainmentsysteem vergrendelen of alle gegevens van dit profiel wissen als te veel onjuiste wachtwoorden worden getypt."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"De schermvergrendeling wijzigen"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Wijzig de schermvergrendeling."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Alle gegevens wissen"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"De gegevens van de tablet zonder waarschuwing wissen door de fabrieksinstellingen te herstellen."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"De gegevens van het Android TV-apparaat zonder waarschuwing wissen door de fabrieksinstellingen terug te zetten."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"De gegevens van het infotainmentsysteem zonder waarschuwing wissen door de fabrieksinstellingen terug te zetten."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Wis de gegevens van de telefoon zonder waarschuwing door de fabrieksinstellingen te herstellen."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Gebruikersgegevens wissen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profielgegevens wissen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Gebruikersgegevens wissen"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"De gegevens van deze gebruiker op deze tablet zonder waarschuwing wissen."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"De gegevens van deze gebruiker op dit Android TV-apparaat zonder waarschuwing wissen."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"De gegevens van dit profiel op het infotainmentsysteem zonder waarschuwing wissen."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"De gegevens van deze gebruiker op deze telefoon zonder waarschuwing wissen."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Algemene proxy voor het apparaat instellen"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"De algemene proxy voor het apparaat instellen die moet worden gebruikt terwijl het beleid is geactiveerd. Alleen de eigenaar van het apparaat kan de algemene proxy instellen."</string>
@@ -1712,7 +1718,7 @@
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tik op een functie om deze te gebruiken:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Functies kiezen voor gebruik met de knop Toegankelijkheid"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Functies kiezen voor gebruik met de sneltoets via de volumeknop"</string>
- <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> staat uit"</string>
+ <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is uitgezet"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Snelkoppelingen bewerken"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Sneltoets uitzetten"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index bb68990..5b536a5 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ଆପଣଙ୍କ ଫେସ୍ ମଡେଲକୁ ଡିଲିଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ, ତା\'ପରେ ପୁଣି ଆପଣଙ୍କ ଫେସ୍ ଯୋଗ କରନ୍ତୁ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ଫେସ୍ ଅନଲକ୍ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ଫୋନକୁ ଦେଖି ଏହାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ଫେସ ଅନଲକ ବ୍ୟବହାର କରିବା ପାଇଁ, ସେଟିଂସ ଏବଂ ଗୋପନୀୟତାରେ "<b>"କ୍ୟାମେରା ଆକ୍ସେସ"</b>"କୁ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ଅନଲକ୍ କରିବା ପାଇଁ ଆହୁରି ଅଧିକ ଉପାୟ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ଏକ ଟିପଚିହ୍ନ ଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ୍"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ସ୍କ୍ରୀନ୍-ଅନଲକ୍ କରିବା ଉଦ୍ୟମ ନୀରିକ୍ଷଣ କରନ୍ତୁ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଟାବଲେଟ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ସ୍କ୍ରିନ ଅନଲକ କରିବା ସମୟରେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ମନିଟର କରନ୍ତୁ ଏବଂ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମକୁ ଲକ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମର ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଫୋନ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ସ୍କ୍ରିନ ଅନଲକ କରିବା ସମୟରେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ମନିଟର କରନ୍ତୁ ଏବଂ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମକୁ ଲକ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଏହି ପ୍ରୋଫାଇଲର ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ସ୍କ୍ରିନ୍ ଲକ୍ ବଦଳାଏ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ସ୍କ୍ରିନ୍ ଲକ୍ ବଦଳାଏ।"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ସମସ୍ତ ଡାଟା ଖାଲି କରିବା"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ସେଟିଙ୍ଗ କରାଇ ଟାବ୍ଲେଟ୍ର ଡାଟା ଲିଭାଇଥାଏ।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ବିନା ଚେତାବନୀରେ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ର ଡାଟା ଲିଭାନ୍ତୁ।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ କରି ବିନା ଚେତାବନୀରେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମର ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ଫୋନ୍ର ଡାଟା ଲିଭାଇଥାଏ।"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ୟୁଜର୍ ଡାଟା ଲିଭାନ୍ତୁ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ପ୍ରୋଫାଇଲ ଡାଟା ଖାଲି କରନ୍ତୁ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ୟୁଜର୍ ଡାଟା ଲିଭାନ୍ତୁ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ବିନା ଚେତାବନୀରେ ଏହି ଟାବଲେଟରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ବିନା ଚେତାବନୀରେ ଏହି Android TV ଡିଭାଇସ୍ରେ ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ବିନା ଚେତାବନୀରେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମରେ ଥିବା ଏହି ପ୍ରୋଫାଇଲର ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ବିନା ଚେତାବନୀରେ ଏହି ଫୋନରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ଗ୍ଲୋବଲ୍ ପ୍ରକ୍ସୀ ଡିଭାଇସ୍କୁ ସେଟ୍ କରନ୍ତୁ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ପଲିସୀ ସକ୍ଷମ କରାଯାଇଥିବାବେଳେ ବ୍ୟବହାର କରିବା ପାଇଁ ଗ୍ଲୋବାଲ୍ ପ୍ରକ୍ସୀ ସେଟ୍ କରନ୍ତୁ। କେବଳ ଡିଭାଇସ୍ ମାଲିକ ଗ୍ଲୋବାଲ୍ ପ୍ରକ୍ସୀ ସେଟ୍ କରିପାରିବେ।"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 9b6129f..002dcf8 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ਆਪਣੇ ਚਿਹਰੇ ਦਾ ਮਾਡਲ ਮਿਟਾਉਣ ਲਈ ਟੈਪ ਕਰੋ, ਫਿਰ ਆਪਣਾ ਚਿਹਰਾ ਦੁਬਾਰਾ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ਫ਼ੇਸ ਅਣਲਾਕ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ਆਪਣੇ ਫ਼ੋਨ ਵੱਲ ਦੇਖ ਕੇ ਇਸਨੂੰ ਅਣਲਾਕ ਕਰੋ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ਫ਼ੇਸ ਅਣਲਾਕ ਨੂੰ ਵਰਤਣ ਲਈ, ਸੈਟਿੰਗਾਂ > ਪਰਦੇਦਾਰੀ ਵਿੱਚ ਜਾ ਕੇ "<b>"ਕੈਮਰਾ ਪਹੁੰਚ"</b>" ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ਅਣਲਾਕ ਕਰਨ ਦੇ ਹੋਰ ਤਰੀਕਿਆਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ਸਕ੍ਰੀਨ ਅਣਲਾਕ ਕਰਨ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ \'ਤੇ ਨਿਗਰਾਨੀ ਰੱਖੋ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ਸਕ੍ਰੀਨ ਲਾਕ ਬਦਲੋ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ਸਕ੍ਰੀਨ ਲਾਕ ਬਦਲੋ।"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ਇੱਕ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਟੈਬਲੈੱਟ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਤੁਹਾਡੇ Android TV ਡੀਵਾਈਸ ਦਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ਇੱਕ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਫ਼ੋਨ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ਉਪਭੋਗਤਾ ਡਾਟਾ ਮਿਟਾਓ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ਪ੍ਰੋਫਾਈਲ ਡਾਟਾ ਮਿਟਾਓ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ਉਪਭੋਗਤਾ ਡਾਟਾ ਮਿਟਾਓ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ ਟੈਬਲੈੱਟ ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ Android TV ਡੀਵਾਈਸ \'ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ਇਸ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ \'ਤੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ ਫ਼ੋਨ ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ਡੀਵਾਈਸ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰੋ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ਜਦੋਂ ਨੀਤੀ ਚਾਲੂ ਹੋਵੇ ਤਾਂ ਵਰਤੇ ਜਾਣ ਲਈ ਡੀਵਾਈਸ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰੋ। ਕੇਵਲ ਡੀਵਾਈਸ ਮਾਲਕ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰ ਸਕਦਾ ਹੈ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 586a03a..52182f5 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Kliknij, aby usunąć model twarzy, a następnie ponownie dodaj skan twarzy"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Skonfiguruj rozpoznawanie twarzy"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Popatrz na ekran telefonu, aby go odblokować"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aby używać rozpoznawania twarzy, włącz "<b>"dostęp do aparatu"</b>" w Ustawieniach i prywatności"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Skonfiguruj więcej sposobów odblokowywania"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Kliknij, aby dodać odcisk palca"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odblokowywanie odciskiem palca"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorowanie prób odblokowania ekranu"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Przy odblokowywaniu ekranu monitoruj, ile razy wpisano nieprawidłowe hasło i blokuj tablet lub usuń z niego wszystkie dane, jeśli nieprawidłowe hasło podano zbyt wiele razy."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorowanie liczby nieudanych prób odblokowania ekranu za pomocą hasła oraz blokowanie urządzenia z Androidem TV lub kasowanie z niego wszystkich danych w razie wpisania błędnego hasła zbyt wiele razy."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorowanie przypadków nieprawidłowego wpisania hasła podczas odblokowywania ekranu i blokowanie systemu multimedialno-rozrywkowego lub usuwanie z niego wszystkich danych przy zbyt dużej liczbie błędnych prób."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Przy odblokowywaniu ekranu monitoruje, ile razy wpisano nieprawidłowe hasło, i blokuje telefon lub usuwa z niego wszystkie dane, jeśli nieprawidłowe hasło podano zbyt wiele razy"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorowanie, ile razy wpisano błędne hasło podczas odblokowywania ekranu, oraz blokowanie tabletu albo kasowanie wszystkich danych tego użytkownika, gdy zbyt wiele razy wpisano błędne hasło."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorowanie, ile razy wpisano błędne hasło podczas odblokowywania ekranu, oraz blokowanie urządzenia z Androidem TV albo kasowanie wszystkich danych tego użytkownika, gdy błędne hasło zostało wpisane zbyt wiele razy."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorowanie przypadków nieprawidłowego wpisania hasła podczas odblokowywania ekranu i blokowanie systemu multimedialno-rozrywkowego lub usuwanie wszystkich danych z profilu przy zbyt dużej liczbie błędnych prób."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorowanie, ile razy wpisano błędne hasło podczas odblokowywania ekranu, oraz blokowanie telefonu albo kasowanie wszystkich danych tego użytkownika, gdy zbyt wiele razy wpisano błędne hasło."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Zmiana blokady ekranu"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Pozwala zmienić blokadę ekranu."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Usuwanie wszystkich danych"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Wymazywanie danych z tabletu bez ostrzeżenia przez przywrócenie danych fabrycznych"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Wymazywanie danych z urządzenia z Androidem TV bez ostrzeżenia przez przywrócenie danych fabrycznych."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Wykasowanie danych z systemu multimedialno-rozrywkowego bez ostrzeżenia przez przywrócenie danych fabrycznych."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Wymazywanie danych z telefonu bez ostrzeżenia przez przywrócenie danych fabrycznych."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Kasuj dane użytkownika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Wykasuj dane z profilu"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Kasuj dane użytkownika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Kasowanie danych tego użytkownika na tym tablecie bez ostrzeżenia."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Kasowanie danych tego użytkownika na tym urządzeniu z Androidem TV bez ostrzeżenia."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Wykasowanie danych z profilu w tym systemie multimedialno-rozrywkowym bez ostrzeżenia."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Kasowanie danych tego użytkownika na tym telefonie bez ostrzeżenia."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Ustaw globalny serwer proxy urządzenia"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Ustawianie globalnego serwera proxy urządzenia do użycia przy włączonych zasadach. Tylko właściciel urządzenia może ustawić globalny serwer proxy."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index da3164a..648ca00 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações > Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorar tentativas de desbloqueio de tela"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o dispositivo Android TV ou limpa todos os dados nele se muitas senhas incorretas forem digitadas."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o sistema de infoentretenimento ou apaga todos os dados desse sistema quando isso acontece muitas vezes."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o tablet ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o dispositivo Android TV ou apaga todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o sistema de infoentretenimento ou apaga todos os dados do perfil quando isso acontece muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o smartphone ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Alterar o bloqueio de tela"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Altera o bloqueio de tela."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Apague os dados do tablet sem aviso redefinindo a configuração original."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Redefine o dispositivo Android TV para a configuração original e apaga os dados sem aviso."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Apaga os dados sem aviso, redefinindo o sistema de infoentretenimento para a configuração original."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Apaga os dados sem aviso redefinindo o smartphone para a configuração original."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Limpar dados do usuário"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Apagar dados do perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Limpar dados do usuário"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Limpa os dados do usuário neste tablet sem aviso prévio."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Limpa os dados do usuário neste dispositivo Android TV sem aviso prévio."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Apaga os dados do perfil do sistema de infoentretenimento sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Limpa os dados do usuário neste smartphone sem aviso prévio."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir o proxy global do dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Configura o proxy global do dispositivo para ser usado enquanto a política está ativada. Somente o proprietário do dispositivo pode definir o proxy global."</string>
@@ -1194,7 +1200,7 @@
<string name="whichOpenLinksWith" msgid="1120936181362907258">"Abrir links com"</string>
<string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Abrir links com <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Abrir links do domínio <xliff:g id="HOST">%1$s</xliff:g> usando <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
- <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Conceder acesso"</string>
+ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permitir acesso"</string>
<string name="whichEditApplication" msgid="6191568491456092812">"Editar com"</string>
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editar com %1$s"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editar"</string>
@@ -2096,12 +2102,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi rebaixada a Silenciosa. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação foi classificada com maior prioridade. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação foi classificada com menor prioridade. Toque para enviar seu feedback."</string>
- <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações aprimoradas"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações aprimoradas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
+ <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações avançadas"</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações avançadas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desativar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações aprimoradas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações aprimoradas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 9163bd7..7e8d593 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para eliminar o seu modelo de rosto e, em seguida, adicione o seu rosto novamente"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configure o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o telemóvel ao olhar para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para utilizar o Desbloqueio facial, ative o "<b>"Acesso à câmara"</b>" em Definições > Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorizar tentativas de desbloqueio do ecrã"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitorizar o número de palavras-passe incorretas escritas ao desbloquear o ecrã e bloquear o tablet ou apagar todos os dados do tablet, se forem escritas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o seu dispositivo Android TV ou apagar todos os dados do mesmo se forem introduzidas demasiadas palavras-passe incorretas."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorize o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloqueie o sistema de infoentretenimento ou apague todos os dados do sistema de infoentretenimento, se forem escritas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o telemóvel ou apagar todos os dados do telemóvel caso tenham sido introduzidas demasiadas palavras-passe."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o tablet ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o dispositivo Android TV ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorize o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloqueie o sistema de infoentretenimento ou apague todos os dados deste utilizador, se forem introduzidas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o telemóvel ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Alterar o bloqueio de ecrã"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Altera o bloqueio de ecrã."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Apagar os dados do tablet sem avisar através de uma reposição de dados de fábrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Apagar os dados do seu dispositivo Android TV sem avisar ao efetuar uma reposição de dados de fábrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Apague os dados do sistema de infoentretenimento sem aviso ao executar uma reposição de dados de fábrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Apaga os dados do telemóvel sem avisar ao efetuar uma reposição de dados de fábrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Apagar os dados do utilizador"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Apague os dados do perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Apagar os dados do utilizador"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Apagar os dados deste utilizador neste tablet sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Apagar os dados deste utilizador neste dispositivo Android TV sem aviso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Apague os dados deste perfil neste sistema de infoentretenimento sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Apagar os dados deste utilizador neste telemóvel sem aviso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir o proxy global do aparelho"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Definir o proxy global do dispositivo a utilizar enquanto a política está ativada. Apenas o proprietário do dispositivo pode definir o proxy global."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index da3164a..648ca00 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações > Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorar tentativas de desbloqueio de tela"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o dispositivo Android TV ou limpa todos os dados nele se muitas senhas incorretas forem digitadas."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o sistema de infoentretenimento ou apaga todos os dados desse sistema quando isso acontece muitas vezes."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o tablet ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o dispositivo Android TV ou apaga todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o sistema de infoentretenimento ou apaga todos os dados do perfil quando isso acontece muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o smartphone ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Alterar o bloqueio de tela"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Altera o bloqueio de tela."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Apague os dados do tablet sem aviso redefinindo a configuração original."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Redefine o dispositivo Android TV para a configuração original e apaga os dados sem aviso."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Apaga os dados sem aviso, redefinindo o sistema de infoentretenimento para a configuração original."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Apaga os dados sem aviso redefinindo o smartphone para a configuração original."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Limpar dados do usuário"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Apagar dados do perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Limpar dados do usuário"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Limpa os dados do usuário neste tablet sem aviso prévio."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Limpa os dados do usuário neste dispositivo Android TV sem aviso prévio."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Apaga os dados do perfil do sistema de infoentretenimento sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Limpa os dados do usuário neste smartphone sem aviso prévio."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir o proxy global do dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Configura o proxy global do dispositivo para ser usado enquanto a política está ativada. Somente o proprietário do dispositivo pode definir o proxy global."</string>
@@ -1194,7 +1200,7 @@
<string name="whichOpenLinksWith" msgid="1120936181362907258">"Abrir links com"</string>
<string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Abrir links com <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Abrir links do domínio <xliff:g id="HOST">%1$s</xliff:g> usando <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
- <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Conceder acesso"</string>
+ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permitir acesso"</string>
<string name="whichEditApplication" msgid="6191568491456092812">"Editar com"</string>
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editar com %1$s"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editar"</string>
@@ -2096,12 +2102,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi rebaixada a Silenciosa. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação foi classificada com maior prioridade. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação foi classificada com menor prioridade. Toque para enviar seu feedback."</string>
- <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações aprimoradas"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações aprimoradas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
+ <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações avançadas"</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações avançadas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desativar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações aprimoradas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações aprimoradas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 2febb26..c1559dd 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -619,6 +619,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Atingeți pentru a șterge modelul facial, apoi adăugați din nou fața"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurați Deblocarea facială"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Deblocați-vă telefonul uitându-vă la acesta"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pentru a folosi Deblocarea facială, activați "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurați mai multe moduri de deblocare"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Atingeți ca să adăugați o amprentă"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Deblocare cu amprenta"</string>
@@ -732,9 +733,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Să monitorizeze încercările de deblocare a ecranului"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți datele acesteia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați dispozitivul Android TV sau ștergeți toate datele de pe acesta dacă se introduc prea multe parole incorecte."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați sistemul de infotainment sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați dispozitivul Android TV sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați sistemul de infotainment sau ștergeți toate datele acestui profil dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Să schimbe blocarea ecranului"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Modificați blocarea ecranului."</string>
@@ -743,10 +746,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Să șteargă toate datele"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ștergeți datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ștergeți datele de pe dispozitivul Android TV fără avertisment, efectuând o revenire la setările din fabrică."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ștergeți datele din sistemul de infotainment fără avertisment, prin revenirea la setările din fabrică."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ștergeți datele din telefon fără avertisment, efectuând resetarea configurării din fabrică."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Ștergeți datele utilizatorului"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Ștergeți datele de profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Ștergeți datele utilizatorului"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ștergeți datele acestui utilizator de pe această tabletă fără avertisment."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ștergeți datele acestui utilizator de pe acest dispozitiv Android TV fără avertisment"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ștergeți datele profilului din acest sistem de infotainment fără avertisment."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ștergeți datele acestui utilizator de pe acest telefon fără avertisment."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Setați serverul proxy global pentru dispozitiv"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Setați serverul proxy global pentru dispozitiv, care să fie utilizat cât timp politica este activă. Numai proprietarul dispozitivului poate seta serverul proxy global."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 1055a6d..8020cdd 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Нажмите, чтобы удалить модель лица, а затем добавьте ее снова."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Настройка фейсконтроля"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Вы сможете разблокировать телефон, просто посмотрев на него."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Чтобы использовать фейсконтроль, разрешите "<b>"доступ к камере"</b>". Для этого перейдите в настройки и нажмите \"Конфиденциальность\"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройте другие способы разблокировки"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Нажмите, чтобы добавить отпечаток пальца."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Разблокировка по отпечатку пальца"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Отслеживание попыток разблокировать экран"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Отслеживает попытки ввода пароля при разблокировке экрана и блокирует планшетный ПК или удаляет с него все данные, если было сделано слишком много таких попыток."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Блокировать устройство Android TV или удалять с него все ваши данные при слишком большом количестве неудачных попыток ввести пароль для разблокировки экрана."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Блокировать информационно-развлекательную систему или удалять из нее все данные, если совершено слишком много неудачных попыток ввести пароль для разблокировки экрана."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Отслеживает попытки ввода пароля при разблокировке экрана и блокирует телефон или удаляет с него все данные, если было сделано слишком много таких попыток."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Отслеживать неверно введенные пароли при разблокировке экрана и блокировать планшет или удалять с него все данные, если сделано слишком много неудачных попыток."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Блокировать устройство Android TV или удалять с него все данные этого пользователя при слишком большом количестве неудачных попыток ввести пароль для разблокировки экрана."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Блокировать информационно-развлекательную систему или удалять все данные профиля, если совершено слишком много неудачных попыток ввести пароль для разблокировки экрана."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Отслеживать неверно введенные пароли при разблокировке экрана и блокировать телефон или удалять с него все данные, если сделано слишком много неудачных попыток."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Изменение способа блокировки экрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Изменять способ блокировки экрана."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Удаление всех данных"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Удалять все данные на планшетном ПК без предупреждения путем сброса настроек."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Удалять данные с устройства Android TV без предупреждения, выполняя восстановление заводских настроек."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Сбрасывать настройки без предупреждения, таким образом удаляя все данные из информационно-развлекательной системы."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Удалять все данные на телефоне без предупреждения путем сброса настроек."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Удалить пользовательские данные"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Удаление данных профиля"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Удалить пользовательские данные"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Удалить данные этого пользователя с планшета без предупреждения."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Удалять данные этого пользователя с устройства Android TV без предупреждения."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Удалять данные профиля из этой информационно-развлекательной системы без предупреждения."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Удалить данные этого пользователя с телефона без предупреждения."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Глобальный прокси-сервер"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Настроить глобальный прокси-сервер устройства, который будет использоваться при активной политике. Это может сделать только владелец устройства."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 703ad17..7408dc0 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ඔබගේ මුහුණත ආකෘතිය මැකීමට තට්ටු කරන්න, අනතුරුව ඔබගේ මුහුණ නැවත එක් කරන්න"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"මුහුණෙන් අගුළු හැරීම පිහිටුවන්න"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ඔබගේ දුරකථනය දෙස බැලීමෙන් එහි අගුලු හරින්න"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"මුහුණෙන් අගුලු හැරීම භාවිත කිරීමට, සැකසීම් > පෞද්ගලිකත්වය තුළ "<b>"කැමරා ප්රවේශය"</b>" ක්රියාත්මක කරන්න"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"අගුලු හැරීමට තවත් ක්රම සකසන්න"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ඇඟිලි සලකුණක් එක් කිරීමට තට්ටු කරන්න"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ඇඟිලි සලකුණු අගුළු හැරීම"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"තිරය අගුළු ඇරීමේ උත්සාහයන් නිරීක්ෂණය කරන්න"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"තිරය අගුළු හැරීමේදී වැරදියට ටයිප් කළ මුරපද ගණන නිරීක්ෂණය කරන්න සහ ටැබ්ලටය අගුළු දමන්න හෝ වැරදි මුරපද බොහෝ ගණනක් ටයිප් කර ඇති නම් ටැබ්ලටයේ සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් ඔබේ Android TV උපාංගය අගුලු දමන්න නැතහොත් මෙම Android TV උපාංගයෙහි සියලු දත්ත මකන්න."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් තොරතුරු විනෝදාස්වාද පද්ධතිය අගුලු දමන්න නැතහොත් මෙම තොරතුරු විනෝදාස්වාද පද්ධතියෙහි සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"තිරය අගුළු හැරීමේදී වැරදියට ටයිප් කළ මුරපද ගණන නිරීක්ෂණය කරන්න සහ දුරකථනය අගුළු දමන්න හෝ වැරදි මුරපද බොහෝ ගණනක් ටයිප් කර ඇති නම් දුරකථනයේ සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් ටැබ්ලටය අගුලු දමන්න නැතහොත් මෙම පරිශීලකයාගේ සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් ඔබේ Android TV අගුලු දමන්න නැතහොත් මෙම පරිශීලකයාගේ සියලු දත්ත මකන්න."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් තොරතුරු විනෝදාස්වාද පද්ධතිය අගුලු දමන්න නැතහොත් මෙම පැතිකඩෙහි සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් දුරකථනය අගුලු දමන්න නැතහොත් මෙම පරිශීලකයාගේ සියලු දත්ත මකන්න."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"තිර අගුල වෙනස් කරන්න"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"තිර අගුල වෙනස් කරන්න."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"සියලු දත්ත මකන්න"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"කර්මාන්ත ශාලා දත්ත යළි පිහිටුවීමෙන් පසුව අනතුරු ඇඟවිමකින් තොරවම ටැබ්ලට් දත්ත මකා දමයි."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"කර්මාන්ත ශාලා දත්ත යළි සැකසීමක් සිදු කිරීම මගින්, අනතුරු ඇඟවිමකින් තොරව ඔබේ Android TV දත්ත මකයි."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"අනතුරු ඇඟවීමෙන් තොරව කර්මාන්තශාලා දත්ත යළි සැකසීමක් සිදු කිරීම මගින් තොරතුරු විනෝදාස්වාද පද්ධතියේ දත්ත මකන්න."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"කර්මාන්ත ශාලා දත්ත යළි පිහිටුවීමෙන් පසුව අනතුරු ඇඟවිමකින් තොරවම දුරකථන දත්ත මකා දමයි."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"පරිශීලක දත්ත මකන්න"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"පැතිකඩ දත්ත මකන්න"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"පරිශීලක දත්ත මකන්න"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"අනතුරු ඇඟවීමකින් තොරව මෙම ටැබ්ලටයෙහි මෙම පරිශීලකයාගේ දත්ත මැකීම."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"අනතුරු ඇඟවීමකින් තොරව මෙම Android TV උපාංගයෙහි මෙම පරිශීලකයාගේ දත්ත මැකීම."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"අනතුරු ඇඟවීමෙන් තොරව මෙම තොරතුරු විනෝදාස්වාද පද්ධතියේ මෙම පැතිකඩෙහි දත්ත මකන්න."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"අනතුරු ඇඟවීමකින් තොරව මෙම දුරකථනයෙහි මෙම පරිශීලකයාගේ දත්ත මැකීම."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"උපාංග ගෝලීය නියුතුව සකස් කිරීම"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ප්රතිපත්තිය සක්රිය අතරතුර ගෝලීය ප්රොක්සි භාවිත කිරීමට උපාංගය සකසන්න. උපාංග හිමිකරුට පමණක් ගෝලීය ප්රොක්සි සැකසිය හැකිය."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 334203c..17619c3 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Klepnutím odstráňte model tváre a potom znova pridajte svoju tvár"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odomknutie tvárou"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Odomykajte telefón tak, že sa naň pozriete"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ak chcete používať odomknutie tvárou, v sekcii Nastavenia > Ochrana súkromia zapnite "<b>"prístup ku kamere"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte viac spôsobov odomknutia"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Klepnutím pridajte odtlačok prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odomknutie odtlačkom prsta"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovanie pokusov o odomknutie obrazovky"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Sledovať počet nesprávnych hesiel zadaných pri odomykaní obrazovky a zamknúť tablet alebo vymazať všetky údaje tabletu v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Sledovanie počtu nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknutie zariadenia Android TV alebo vymazanie všetkých jeho údajov."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a uzamknite palubný systém alebo vymažte všetky údaje v palubnom systéme v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Sledovať počet nesprávnych hesiel zadaných pri odomykaní obrazovky a zamknúť telefón alebo vymazať všetky údaje v telefóne v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknite tablet alebo vymažte všetky údaje tohto používateľa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Sledovanie počtu nesprávnych hesiel zadaných pri odomykaní obrazovky a ak je ich zadaných príliš mnoho, uzamknutie zariadenia Android TV alebo vymazanie všetkých údajov tohto používateľa."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknite palubný systém alebo vymažte všetky údaje tohto profilu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknite telefón alebo vymažte všetky údaje tohto používateľa."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Zmeniť zámku obrazovky"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Zmeniť zámku obrazovky."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Vymazať všetky dáta"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Bez predchádzajúceho upozornenia vymazať všetky dáta obnovením výrobných nastavení tabletu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Vymazanie údajov v zariadení Android TV bez upozornenia obnovením výrobných nastavení."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Vymažte údaje palubného systému bez upozornenia obnovením výrobných nastavení."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Bez predchádzajúceho upozornenia vymazať všetky dáta obnovením výrobných nastavení telefónu."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Vymazať údaje používateľa"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Vymazanie údajov profilu"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Vymazať údaje používateľa"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Vymažte bez upozornenia údaje tohto používateľa na tomto tablete."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Vymazanie údajov tohto používateľa v tomto zariadení Android TV bez upozornenia."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Vymažte údaje tohto profilu v tomto palubnom systéme bez upozornenia."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Vymažte bez upozornenia údaje tohto používateľa na tomto telefóne."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Nastaviť globálny server proxy zariadenia"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Vyberte globálny proxy server, ktorý sa bude používať po aktivácii pravidiel. Nastaviť ho môže iba vlastník zariadenia."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7e92ebf..e998283 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dotaknite se, da izbrišete model obraza, in nato znova dodajte obraz."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavitev odklepanja z obrazom"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Odklenite telefon tako, da ga pogledate."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Če želite uporabljati odklepanje z obrazom, v meniju »Nastavitve« > »Zasebnost« vklopite možnost "<b>"Dostop do fotoaparata"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavite več načinov odklepanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dotaknite se, da dodate prstni odtis."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odklepanje s prstnim odtisom"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadzor nad poskusi odklepanja zaslona"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Nadzoruje število nepravilno vnesenih gesel pri odklepanju zaslona in zaklene tablični računalnik ali izbriše vse podatke v njem, če je vnesenih preveč nepravilnih gesel."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Nadzira število vnesenih nepravilnih gesel pri odklepanju zaslona in zaklene napravo Android TV ali izbriše vse podatke v napravi Android TV, če je vnesenih preveč nepravilnih gesel."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Spremljanje števila nepravilno vnesenih gesel pri odklepanju zaslona in zaklenitev informativno-razvedrilnega sistema ali izbris vseh podatkov v njem v primeru preveč vnosov nepravilnih gesel"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Spremljajte število vnesenih napačnih gesel, s katerimi želite odkleniti zaslon. Če je teh vnosov preveč, zaklenite telefon ali izbrišite vse podatke v njem."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Nadzira število vnesenih nepravilnih gesel pri odklepanju zaslona in zaklene tablični računalnik ali izbriše vse podatke lastnika, če je vnesenih preveč nepravilnih gesel."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Nadzira število vnesenih nepravilnih gesel pri odklepanju zaslona in zaklene napravo Android TV ali izbriše vse podatke tega uporabnika, če je vnesenih preveč nepravilnih gesel."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Spremljanje števila nepravilno vnesenih gesel pri odklepanju zaslona in zaklenitev informativno-razvedrilnega sistema ali izbris vseh podatkov tega profila v primeru preveč vnosov nepravilnih gesel"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Nadzira število vnesenih nepravilnih gesel pri odklepanju zaslona in zaklene telefon ali izbriše vse podatke lastnika, če je vnesenih preveč nepravilnih gesel."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Spreminjanje zaklepanja zaslona"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Spremeni zaklepanje zaslona."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje vseh podatkov"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Izbris podatkov v tabličnem računalniku brez opozorila s ponastavitvijo na tovarniške nastavitve"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Brisanje podatkov v napravi Android TV z izvedbo ponastavitve na privzete tovarniške nastavitve brez opozorila."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Izbris podatkov informativno-razvedrilnega sistema brez opozorila s ponastavitvijo na tovarniške nastavitve"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Izbris podatkov v telefonu brez opozorila s ponastavitvijo na tovarniške nastavitve."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Izbris podatkov uporabnika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Izbris podatkov profila"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Izbris podatkov uporabnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Izbris podatkov uporabnika v tem tabličnem računalniku brez opozorila."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Izbris podatkov uporabnika v tej napravi Android TV brez opozorila."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Izbris podatkov tega profila v informativno-razvedrilnem sistemu brez opozorila"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Izbris podatkov uporabnika v tem telefonu brez opozorila."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Nastavitev globalnega strežnika proxy za napravo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Nastavitev globalnega strežnika proxy naprave, ki bo v uporabi, ko je pravilnik omogočen. Samo lastnik naprave lahko nastavi globalni strežnik proxy."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index a303963..4d9290c 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Trokit për të fshirë modelin tënd të fytyrës, pastaj shtoje përsëri fytyrën tënde"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguro \"Shkyçjen me fytyrë\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Shkyçe telefonin duke parë tek ai"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Për të përdorur \"Shkyçjen me fytyrë\", aktivizo "<b>"Qasjen te kamera"</b>" te \"Cilësimet\" > \"Privatësia\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguri më shumë mënyra për të shkyçur"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Trokit për të shtuar një gjurmë gishti"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Shkyçja me gjurmën e gishtit"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitoro tentativat e shkyçjes së ekranit"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitoro numrin e fjalëkalimeve të shkruar gabim kur shkyç ekranin. Kyç tabletin ose fshi të gjitha të dhënat e tij, nëse shkruhen shumë fjalëkalime të pasakta."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç pajisjen tënde Android TV ose spastro të gjitha të dhënat e pajisjes sate Android TV nëse shkruhen gabim shumë fjalëkalime."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç sistemin info-argëtues ose spastro të gjitha të dhënat e tij nëse shkruhen shumë fjalëkalime të gabuara."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitoro numrin e fjalëkalimeve të shkruar gabim kur shkyç ekranin. Kyç telefonin ose fshi të gjitha të dhënat e tij, nëse shkruhen shumë fjalëkalime të pasakta."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin. Kyçe tabletin ose spastro të gjitha të dhënat e këtij përdoruesi nëse shkruhen shumë fjalëkalime të gabuara."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyçe pajisjen tënde Android TV ose spastro të gjitha të dhënat e këtij përdoruesi nëse shkruhen shumë fjalëkalime të gabuara."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç sistemin info-argëtues ose spastro të gjitha të dhënat e këtij profili nëse shkruhen shumë fjalëkalime të gabuara."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin. Kyçe telefonin ose spastro të gjitha të dhënat e këtij përdoruesi nëse shkruhen shumë fjalëkalime të gabuara."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ndryshimin e kyçjes së ekranit"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ndryshon kyçjen e ekranit."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Spastrimin e të gjitha të dhënave"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Fshi të dhënat e tabletit pa paralajmërim duke kryer një rivendosje të të dhënave në gjendje fabrike."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Spastro të dhënat e pajisjes Android TV pa paralajmërim duke kryer një rivendosje të të dhënave në gjendje fabrike."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Spastro të dhënat e sistemit info-argëtues pa paralajmërim kur kryen një rivendosje të të dhënave të fabrikës."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Fshin të dhënat e telefonit pa paralajmërim, duke kryer rivendosje të të dhënave në gjendje fabrike."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Spatro të dhënat e përdoruesit"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Spastro të dhënat e profilit"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Spatro të dhënat e përdoruesit"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Spastroji të dhënat e këtij përdoruesi në këtë tablet pa paralajmërim."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Spastroji të dhënat e këtij përdoruesi në këtë pajisje Android TV pa paralajmërim."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Spastro të dhënat e këtij profili në këtë sistem info-argëtues pa paralajmërim."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Spastroji të dhënat e këtij përdoruesi në këtë telefon pa paralajmërim."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Cakto proxy-in global të pajisjes"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Cakto proxy-in global të pajisjes që të përdoret kur të aktivizohet politika. Vetëm pronari i pajisjes mund ta caktojë proxy-in global."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7a097d3..b551f0e 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -619,6 +619,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Додирните да бисте избрисали модел лица, па поново додајте своје лице"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Подесите откључавање лицем"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Откључајте телефон тако што ћете га погледати"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Да бисте користили откључавање лицем, укључите "<b>"приступ камери"</b>" у одељку Подешавања > Приватност"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Подесите још начина за откључавање"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Додирните да бисте додали отисак прста"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Откључавање отиском прста"</string>
@@ -732,9 +733,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Надгледајте покушаје откључавања екрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Прати број нетачно унетих лозинки приликом откључавања екрана и закључава таблет или брише податке са таблета ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава Android TV уређај или брише све податке са Android TV уређаја ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Прати број нетачно унетих лозинки при откључавању екрана и закључава систем за инфо-забаву или брише све податке са система за инфо-забаву ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Прати број нетачно унетих лозинки при откључавању екрана и закључава телефон или брише све податке са телефона ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава таблет или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава Android TV уређај или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава систем за инфо-забаву или брише све податке овог профила ако се унесе превише нетачних лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава телефон или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Промена закључавања екрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Мења закључавање екрана."</string>
@@ -743,10 +746,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Брисање свих података"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Брисање података на таблету без упозорења ресетовањем на фабричка подешавања."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Брише податке Android TV уређаја без упозорења помоћу ресетовања на фабричка подешавања."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Брише податке на систему за инфо-забаву без упозорења ресетовањем на фабричка подешавања."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Брисање података на телефону без упозорења ресетовањем на фабричка подешавања."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Обриши податке корисника"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Брисање података профила"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Обриши податке корисника"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Брише податке овог корисника на овом таблету без упозорења."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Брише податке овог корисника на овом Android TV уређају без упозорења."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Брише податке овог профила на овом систему за инфо-забаву без упозорења."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Брише податке овог корисника на овом телефону без упозорења."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Подесите глобални прокси сервер уређаја"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Подешава глобални прокси уређаја који ће се користити док су смернице омогућене. Само власник уређаја може да подеси глобални прокси."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 1f1579d..ad3df80 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tryck för att radera ansiktsmodellen och lägg sedan till ansiktet igen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurera ansiktslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås upp telefonen genom att titta på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Om du vill använda ansiktslås aktiverar du "<b>"Kameraåtkomst"</b>" i Inställningar > Integritet"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurera fler sätt att låsa upp"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryck för att lägga till ett fingeravtryck"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingeravtryckslås"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Övervaka försök att låsa upp skärmen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås Android TV-enheten eller rensa all data på Android TV-enheten om för många felaktiga lösenord har skrivits in."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås infotainmentsystemet eller rensa all data från infotainmentsystemet om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås surfplattan eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås Android TV-enheten eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås infotainmentsystemet eller rensa all data från profilen om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås mobilen eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ändra skärmlåset"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ändra skärmlåset."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Radera all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Radera data på Android TV-enheten utan förvarning genom att återställa standardinställningarna."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Rensa data från infotainmentsystemet utan förvarning genom att återställa standardinställningarna."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Radera användaruppgifter"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Rensa profildata"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Radera användaruppgifter"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Rensa användarens uppgifter på den här surfplattan utan förvarning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Radera den här användarens data på den här Android TV-enheten utan förvarning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Rensa profilens data i det här infotainmentsystemet utan förvarning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Rensa användarens data på den här mobilen utan förvarning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Ange global proxyserver"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Ange enhetens globala proxy som ska användas när policyn aktiveras. Det är bara enhetens ägare som kan ange global proxy."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8b2cf77..fa3fcc5 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Gusa ili ufute muundo wa uso wako, kisha uweke uso wako tena"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Weka mipangilio ya Kufungua kwa Uso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Fungua simu yako kwa kuiangalia"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ili utumie kipengele cha kufungua kwa uso, washa kipengele cha "<b>"ufikiaji wa Kamera"</b>" katika Mipangilio na Faragha"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weka mipangilio ya mbinu zaidi za kufungua"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Gusa ili uweke alama ya kidole"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Kufungua kwa Alama ya Kidole"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Kuhesabu mara ambazo skrini inajaribu kufunguliwa"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Kufuatilia idadi ya manenosiri yasiyo sahihi yatakayoingizwa wakati wa kufungua skrini, na kufunga kompyuta kibao au kufuta data yote iliyomo kama manenosiri mengi yasiyo sahihi yataingizwa."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Fuatilia idadi ya manenosiri yasiyo sahihi yanayowekwa wakati wa kufungua skrini na ufunge kifaa chako cha Android TV au ufute data yake yote ikiwa mtumiaji ataweka manenosiri mengi mno yasiyo sahihi."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Fuatilia idadi ya manenosiri yasiyo sahihi yanayowekwa wakati wa kufungua skrini, na ufunge mfumo wa burudani na habari au ufute data yote kwenye mfumo wa burudani na habari ikiwa manenosiri mengi mno yasiyo sahihi yatawekwa."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Kufuatilia idadi ya manenosiri yasiyo sahihi yatakayoingizwa wakati wa kufungua skrini, na kufunga simu au kufuta data yote iliyomo kama manenosiri mengi sana yasiyo sahihi yataingizwa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Fuatilia idadi ya manenosiri yasiyo sahihi yaliyoingizwa wakati wa kufungua skrini, na ufunge kompyuta kibao au ufute data yote ya mtumiaji huyu kama ameingiza manenosiri yasiyo sahihi mara nyingi kupita kiasi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Fuatilia idadi ya manenosiri yasiyo sahihi yanayowekwa wakati wa kufungua skrini na ufunge kifaa chako cha Android TV au ufute data yote ya mtumiaji huyu ikiwa ataweka manenosiri yasiyo sahihi mara nyingi mno."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Fuatilia idadi ya manenosiri yasiyo sahihi yanayowekwa wakati wa kufungua skrini, na ufunge mfumo wa burudani na habari au ufute data yote kwenye wasifu huu ikiwa manenosiri mengi mno yasiyo sahihi yatawekwa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Fuatilia idadi ya manenosiri yasiyo sahihi yaliyoingizwa wakati wa kufungua skrini, na ufunge simu au ufute data yote ya mtumiaji huyu kama ameingiza manenosiri yasiyo sahihi mara nyingi kupita kiasi."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Kubadilisha mbinu ya kufunga skrini"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Kubadilisha mbinu ya kufunga skrini."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Kufuta data yote"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Futa data ya kompyuta kibao bila ilani kwa kurejesha mipangilio ambayo kompyuta ilitoka nayo kiwandani."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Futa data ya kifaa chako cha Android TV bila onyo kwa kurejesha data kiliyotoka nayo kiwandani."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Futa data ya mfumo wa burudani na habari bila onyo kwa kurejesha data iliyotoka nayo kiwandani."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Kufuta data ya simu bila ilani kwa kurejesha mipangilio iliyotoka nayo kiwandani."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Futa data yote ya mtumiaji"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Futa data yote ya wasifu"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Futa data yote ya mtumiaji"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Futa data ya mtumiaji huyu iliyo kwenye kompyuta kibao hii bila ilani."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Futa data ya mtumiaji huyu iliyo kwenye kifaa hiki cha Android TV bila ilani."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Futa data yote ya wasifu kwenye mfumo huu wa burudani na habari bila onyo."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Futa data ya mtumiaji huyu iliyo kwenye simu hii bila ilani."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Weka seva mbadala ya ulimwengu kote ya kifaa"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Weka seva mbadala ya ulimwengu ya kifaa itakayotumika wakati sera imewashwa. Ni mmiliki wa kifaa pekee aneyeweza kuweka seva mbadala ya ulimwengu."</string>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 861e329..624581a 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -56,5 +56,7 @@
to be aligned to one side of the screen when in landscape mode. -->
<bool name="config_enableDynamicKeyguardPositioning">true</bool>
+ <integer name="config_chooser_max_targets_per_row">6</integer>
+
</resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 02ed848..e8f15fd 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -110,4 +110,7 @@
<dimen name="immersive_mode_cling_width">380dp</dimen>
<dimen name="floating_toolbar_preferred_width">544dp</dimen>
+
+ <dimen name="chooser_width">624dp</dimen>
+
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index b27af34..b73fe3f 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -170,7 +170,7 @@
<string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"பாதுகாப்பான இணைப்பை நிறுவ முடியவில்லை."</string>
<string name="httpErrorBadUrl" msgid="754447723314832538">"URL தவறாக உள்ளதால் பக்கத்தைத் திறக்க முடியவில்லை."</string>
<string name="httpErrorFile" msgid="3400658466057744084">"ஃபைலை அணுக முடியவில்லை."</string>
- <string name="httpErrorFileNotFound" msgid="5191433324871147386">"கோரப்பட்ட கோப்பைக் கண்டறிய முடியவில்லை."</string>
+ <string name="httpErrorFileNotFound" msgid="5191433324871147386">"கோரப்பட்ட ஃபைலைக் கண்டறிய முடியவில்லை."</string>
<string name="httpErrorTooManyRequests" msgid="2149677715552037198">"மிக அதிகமான கோரிக்கைகள் செயல்படுத்தப்படுகின்றன. பிறகு முயற்சிக்கவும்."</string>
<string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g> க்கான உள்நுழைவு பிழை"</string>
<string name="contentServiceSync" msgid="2341041749565687871">"ஒத்திசை"</string>
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"முகத் தோற்றப் பதிவைத் தட்டி நீக்கிவிட்டு உங்கள் முகத்தை மீண்டும் சேர்க்கவும்"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"\'முகம் காட்டித் திறத்தல்\' அம்சத்தை அமைத்தல்"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"மொபைலைப் பார்ப்பதன் மூலம் அதை அன்லாக் செய்யலாம்"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"முகம் காட்டித் திறத்தல் அம்சத்தைப் பயன்படுத்த, அமைப்புகள் > தனியுரிமை என்பதற்குச் சென்று "<b>"கேமரா அணுகலை"</b>" இயக்கவும்"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"அன்லாக் செய்ய மேலும் பல வழிகளை அமையுங்கள்"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"கைரேகையைச் சேர்க்கத் தட்டுங்கள்"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"கைரேகை அன்லாக்"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"திரையை அன்லாக் செய்வதற்கான முயற்சிகளைக் கண்காணி"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"திரையைத் திறக்கும்போது உள்ளிட்ட தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும், மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிட்டிருந்தால், டேப்லெட்டைப் பூட்டும் அல்லது டேப்லெட்டின் எல்லா தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"திரையைத் திறக்கும்போது எத்தனை முறை தவறான கடவுச்சொற்களை உள்ளிட்டீர்கள் என்பதைக் கண்காணிக்கும், பலமுறை தவறாக உள்ளிட்டிருந்தால் Android TVயைப் பூட்டும் அல்லது Android TVயின் அனைத்துத் தரவையும் அழிக்கும்."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"திரையை அன்லாக் செய்யும்போது உள்ளிடப்படும் தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும். மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிடப்பட்டிருந்தால் இன்ஃபோடெயின்மென்ட் சிஸ்டமைப் பூட்டும் அல்லது அதன் அனைத்துத் தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"திரையைத் திறக்கும்போது உள்ளிட்ட தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும், மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிட்டிருந்தால், மொபைலைப் பூட்டும் அல்லது மொபைலின் எல்லா தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"திரையைத் திறக்கும் போது, எத்தனை முறை தவறான கடவுச்சொல்லை உள்ளிட்டீர்கள் என்பதைக் கண்காணிக்கிறது மற்றும் கடவுச்சொற்களைப் பல முறை தவறாக உள்ளிட்டால், டேப்லெட்டைப் பூட்டும் அல்லது இந்தப் பயனரின் எல்லா தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"திரையைத் திறக்கும் போது எத்தனை முறை தவறான கடவுச்சொல்லை உள்ளிட்டீர்கள் என்பதைக் கண்காணிப்பதோடு கடவுச்சொற்களைப் பல முறை தவறாக உள்ளிட்டால் Android TVயைப் பூட்டும் அல்லது இந்தப் பயனரின் அனைத்துத் தரவையும் அழிக்கும்."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"திரையை அன்லாக் செய்யும்போது உள்ளிடப்படும் தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும். மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிடப்பட்டிருந்தால் இன்ஃபோடெயின்மென்ட் சிஸ்டமைப் பூட்டும் அல்லது இந்தச் சுயவிவரத்தின் அனைத்துத் தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"திரையைத் திறக்கும் போது, எத்தனை முறை தவறான கடவுச்சொல்லை உள்ளிட்டீர்கள் என்பதைக் கண்காணிக்கிறது மற்றும் கடவுச்சொற்களைப் பல முறை தவறாக உள்ளிட்டால், ஃபோனைப் பூட்டும் அல்லது இந்தப் பயனரின் எல்லா தரவையும் அழிக்கும்."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"திரைப் பூட்டை மாற்றுதல்"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"திரைப் பூட்டை மாற்றும்."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"எல்லா டேட்டாவையும் அழித்தல்"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ஆரம்பநிலைத் தரவு மீட்டமைப்பின் மூலம் எச்சரிக்கை வழங்காமல் டேப்லெட்டின் தரவை அழிக்கலாம்."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"தரவின் ஆரம்பநிலைக்கு மீட்டமைப்பதன் மூலம் எச்சரிக்கை செய்யாமல் Android TVயின் தரவை அழிக்கும்."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"தரவின் ஆரம்பநிலை மீட்டமைப்பைச் செயல்படுத்துவதன் மூலம் எச்சரிக்கை எதுவுமின்றி இன்ஃபோடெயின்மென்ட் சிஸ்டமின் தரவை அழிக்கும்."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ஆரம்பநிலைத் தரவு மீட்டமைப்பின் மூலம் எச்சரிக்கை வழங்காமல் மொபைலின் தரவை அழிக்கலாம்."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"பயனர் தரவை அழி"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"சுயவிவரத் தரவை அழித்தல்"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"பயனர் தரவை அழி"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"எச்சரிக்கை எதுவுமின்றி, டேப்லெட்டில் உள்ள இந்தப் பயனரின் தரவை அழிக்கும்."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"எச்சரிக்கை செய்யாமல் Android TVயில் உள்ள இந்தப் பயனரின் தரவை அழிக்கும்."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"எச்சரிக்கை எதுவுமின்றி இந்த இன்ஃபோடெயின்மென்ட் சிஸ்டத்திலுள்ள இந்தச் சுயவிவரத்தின் தரவை அழிக்கும்."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"எச்சரிக்கை எதுவுமின்றி, ஃபோனில் உள்ள இந்தப் பயனரின் தரவை அழிக்கும்."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"சாதன குளோபல் ப்ராக்ஸியை அமை"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"கொள்கை இயக்கப்பட்டிருக்கும்போது பயன்படுத்த வேண்டிய சாதன குளோபல் ப்ராக்ஸியை அமைக்கவும். சாதன உரிமையாளரால் மட்டுமே குளோபல் ப்ராக்ஸியை அமைக்க முடியும்."</string>
@@ -1505,8 +1511,8 @@
<string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"எப்போதும் இயக்கத்தில் இருக்கும்படி அமைத்த VPN இலிருந்து துண்டிக்கப்பட்டது"</string>
<string name="vpn_lockdown_error" msgid="4453048646854247947">"எப்போதும் ஆனில் இருக்கும்படி அமைத்த VPN உடன் இணைக்க முடியவில்லை"</string>
<string name="vpn_lockdown_config" msgid="8331697329868252169">"நெட்வொர்க் அல்லது VPN அமைப்புகளை மாற்றவும்"</string>
- <string name="upload_file" msgid="8651942222301634271">"கோப்பைத் தேர்வுசெய்"</string>
- <string name="no_file_chosen" msgid="4146295695162318057">"எந்தக் கோப்பும் தேர்வுசெய்யப்படவில்லை"</string>
+ <string name="upload_file" msgid="8651942222301634271">"ஃபலைத் தேர்வுசெய்"</string>
+ <string name="no_file_chosen" msgid="4146295695162318057">"எந்த ஃபைலும் தேர்வுசெய்யப்படவில்லை"</string>
<string name="reset" msgid="3865826612628171429">"மீட்டமை"</string>
<string name="submit" msgid="862795280643405865">"சமர்ப்பி"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"\'வாகனம் ஓட்டும் பயன்முறை’ ஆனில் உள்ளது"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 984eba3..d9a109f 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -420,7 +420,7 @@
<string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"మీ ఫోన్లో నిల్వ చేసి ఉన్న కాంటాక్ట్లకు సంబంధించిన డేటాను సవరించడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్లను అనుమతిస్తుంది."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"కాల్ లాగ్ను చదవడం"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"ఈ యాప్ మీ కాల్ చరిత్రను చదవగలదు."</string>
- <string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్ను వ్రాయడం"</string>
+ <string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్ను రాయడం"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్కమింగ్ మరియు అవుట్గోయింగ్ కాల్స్ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్ను సవరించడానికి యాప్ను అనుమతిస్తుంది. హానికరమైన యాప్లు మీ కాల్ లాగ్ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్కమింగ్ మరియు అవుట్గోయింగ్ కాల్స్కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్ను సవరించడానికి యాప్ని అనుమతిస్తుంది. హానికరమైన యాప్లు మీ కాల్ లాగ్ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్కమింగ్ మరియు అవుట్గోయింగ్ కాల్స్ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్ను సవరించడానికి యాప్ను అనుమతిస్తుంది. హానికరమైన యాప్లు మీ కాల్ లాగ్ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
@@ -546,7 +546,7 @@
<string name="permlab_nfc" msgid="1904455246837674977">"సమీప క్షేత్ర కమ్యూనికేషన్ను నియంత్రించడం"</string>
<string name="permdesc_nfc" msgid="8352737680695296741">"సమీప ఫీల్డ్ కమ్యూనికేషన్ (NFC) ట్యాగ్లు, కార్డులు మరియు రీడర్లతో కమ్యూనికేట్ చేయడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_disableKeyguard" msgid="3605253559020928505">"మీ స్క్రీన్ లాక్ను నిలిపివేయడం"</string>
- <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"కీలాక్ మరియు ఏదైనా అనుబంధించబడిన పాస్వర్డ్ భద్రతను నిలిపివేయడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్ను నిలిపివేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్ను మళ్లీ ప్రారంభిస్తుంది."</string>
+ <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"కీలాక్ను, అలాగే ఏదైనా అనుబంధించబడిన పాస్వర్డ్ సెక్యూరిటీని డిజేబుల్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్ను డిజేబుల్ చేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్ను మళ్లీ ఎనేబుల్ చేస్తుంది."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"స్క్రీన్ లాక్ సంక్లిష్టత రిక్వెస్ట్"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ఇది మీ స్క్రీన్ లాక్ పాస్వర్డ్ సంక్లిష్టత స్థాయి (తీవ్రంగా ఉండాలా, ఓ మోస్తరుగా ఉండాలా, తక్కువ తీవ్రంగా ఉండాలా లేదా అస్సలు తీవ్రత ఉండకూడదా) తెలుసుకోవడానికి యాప్ను అనుమతిస్తుంది, అంటే పొడుగు ఎంత ఉండాలి, ఏ రకమైన స్క్రీన్ లాక్ పధ్ధతి అనుసరించాలో సూచిస్తుంది. అలాగే, స్క్రీన్ లాక్ పాస్వర్డ్ సంక్లిష్టతను ఏ స్థాయికి సెట్ చేసుకుంటే బాగుంటుందో కూడా వినియోగదారులకు యాప్ సూచించగలదు, కానీ వినియోగదారులు నిరభ్యంతరంగా ఆ సూచనలను పట్టించుకోకుండా వారి ఇష్టం మేరకు చక్కగా సెట్ చేసుకోవచ్చు. ఇంకో ముఖ్య విషయం, స్క్రీన్ లాక్ అన్నది సాదా వచన రూపంలో నిల్వ చేయబడదు, కనుక ఖచ్చితమైన పాస్వర్డ్ ఏమిటనేది యాప్కు తెలియదు."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"బయోమెట్రిక్ హార్డ్వేర్ని ఉపయోగించు"</string>
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ఫేస్ మోడల్ను తొలగించడానికి నొక్కండి, ఆపై మీ ముఖాన్ని మళ్లీ జోడించండి"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ఫేస్ అన్లాక్ను సెటప్ చేయండి"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"మీ ఫోన్ను చూడటం ద్వారా దాన్ని అన్లాక్ చేయండి"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ఫేస్ అన్లాక్ను ఉపయోగించడానికి, సెట్టింగ్లు > గోప్యతలో "<b>"కెమెరా యాక్సెస్"</b>"ను ఆన్ చేయండి"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"అన్లాక్ చేయడానికి మరిన్ని మార్గాలను సెటప్ చేయండి"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"వేలిముద్రను జోడించడానికి ట్యాప్ చేయండి"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"వేలిముద్ర అన్లాక్"</string>
@@ -719,7 +720,7 @@
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"క్యారియర్ సేవలకు అనుబంధించడం"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"క్యారియర్ సేవలకు అనుబంధించడానికి హోల్డర్ను అనుమతిస్తుంది. సాధారణ యాప్లకు ఎప్పటికీ అవసరం ఉండదు."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"అంతరాయం కలిగించవద్దును యాక్సెస్ చేయడం"</string>
- <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు రాయడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"యాప్నకు అనుమతి వినియోగాన్ని ప్రారంభించడానికి హోల్డర్ను అనుమతిస్తుంది. సాధారణ యాప్లకు ఎప్పటికీ ఇటువంటి అనుమతి అవసరం ఉండదు."</string>
<string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"అధిక శాంపిల్ రేటు వద్ద సెన్సార్ డేటాను యాక్సెస్ చేయండి"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"స్క్రీన్ అన్లాక్ ప్రయత్నాలను పర్యవేక్షించండి"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"టైప్ చేసిన చెల్లని పాస్వర్డ్ల సంఖ్యను పర్యవేక్షిస్తుంది. స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు, అనేక సార్లు చెల్లని పాస్వర్డ్లను టైప్ చేస్తే టాబ్లెట్ లాక్ చేయబడుతుంది లేదా టాబ్లెట్లోని మొత్తం డేటా ఎరేజ్ చేయబడుతుంది."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్లను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది, అలాగే చాలా ఎక్కువసార్లు పాస్వర్డ్లను తప్పుగా టైప్ చేసి ఉంటే మీ Android TV పరికరాన్ని లాక్ చేస్తుంది లేదా మీ Android TV డేటా మొత్తాన్ని తొలగిస్తుంది."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు, పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది. ఒకవేళ చాలా ఎక్కువ సార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే, సమాచారంతో కూడిన వినోదం సిస్టమ్ను లాక్ చేస్తుంది లేదా సమాచారంతో కూడిన వినోదం సిస్టమ్ డేటాను తొలగించి ఫ్యాక్టరీ రీసెట్ చేస్తుంది."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"టైప్ చేసిన చెల్లని పాస్వర్డ్ల సంఖ్యను పర్యవేక్షిస్తుంది. స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు, అనేక సార్లు చెల్లని పాస్వర్డ్లను టైప్ చేస్తే ఫోన్ లాక్ చేయబడుతుంది లేదా ఫోన్లోని మొత్తం డేటా ఎరేజ్ చేయబడుతుంది."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది మరియు చాలా ఎక్కువసార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే టాబ్లెట్ను లాక్ చేస్తుంది లేదా ఈ వినియోగదారు యొక్క మొత్తం డేటాను తీసివేస్తుంది."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది, చాలా ఎక్కువసార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే మీ Android TV పరికరాన్ని లాక్ చేస్తుంది లేదా ఈ వినియోగదారు యొక్క మొత్తం డేటాను తీసివేస్తుంది."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది. ఒకవేళ చాలా ఎక్కువ సార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే, సమాచారంతో కూడిన వినోదం సిస్టమ్ను లాక్ చేస్తుంది లేదా ఈ ప్రొఫైల్కు సంబంధించిన డేటాను తొలగించి ఫ్యాక్టరీ రీసెట్ చేస్తుంది."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది మరియు చాలా ఎక్కువసార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే ఫోన్ను లాక్ చేస్తుంది లేదా ఈ వినియోగదారు యొక్క మొత్తం డేటాను తీసివేస్తుంది."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"స్క్రీన్ లాక్ మార్చడానికి"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"స్క్రీన్ లాక్ని మారుస్తుంది."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"మొత్తం డేటాను ఎరేజ్ చేయడానికి"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ఫ్యాక్టరీ డేటా రీసెట్ను అమలు చేయడం ద్వారా హెచ్చరించకుండానే టాబ్లెట్ డేటాను ఎరేజ్ చేయండి."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"హెచ్చరించకుండానే మీ Android TV పరికరం డేటాను ఫ్యాక్టరీ డేటా రీసెట్ ద్వారా తొలగిస్తుంది."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ఫ్యాక్టరీ డేటా రీసెట్ను అమలు చేయడం ద్వారా, హెచ్చరిక లేకుండానే సమాచారంతో కూడిన వినోదం సిస్టమ్ డేటాను తొలగించి ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ఫ్యాక్టరీ డేటా రీసెట్ను అమలు చేయడం ద్వారా హెచ్చరించకుండానే ఫోన్ డేటాను ఎరేజ్ చేస్తుంది."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"వినియోగదారు డేటాను తీసివేయండి"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ప్రొఫైల్ డేటాను తొలగించండి"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"వినియోగదారు డేటాను తీసివేయండి"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"హెచ్చరిక లేకుండానే ఈ టాబ్లెట్లో ఈ వినియోగదారు డేటాను తీసివేస్తుంది."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"హెచ్చరిక లేకుండానే ఈ Android TV పరికరంలో ఈ వినియోగదారు డేటాను తీసివేస్తుంది."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"హెచ్చరిక లేకుండానే ఈ సమాచారంతో కూడిన వినోదం సిస్టమ్లోని ఈ ప్రొఫైల్ డేటా తొలగించబడుతుంది."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"హెచ్చరిక లేకుండానే ఈ ఫోన్లో ఈ వినియోగదారు డేటాను తీసివేస్తుంది."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"పరికరం గ్లోబల్ ప్రాక్సీని సెట్ చేయండి"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"విధానాన్ని ప్రారంభించినప్పుడు ఉపయోగించడానికి పరికర గ్లోబల్ ప్రాక్సీని సెట్ చేస్తుంది. పరికర యజమాని మాత్రమే గ్లోబల్ ప్రాక్సీని సెట్ చేయగలరు."</string>
@@ -1007,7 +1013,7 @@
<string name="autofill_emirate" msgid="2544082046790551168">"ఎమిరేట్"</string>
<string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"మీ వెబ్ బుక్మార్క్లు మరియు చరిత్రను చదవడం"</string>
<string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"బ్రౌజర్ సందర్శించిన అన్ని URLల చరిత్ర గురించి మరియు అన్ని బ్రౌజర్ బుక్మార్క్ల గురించి చదవడానికి యాప్ను అనుమతిస్తుంది. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
- <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"వెబ్ బుక్మార్క్లు మరియు చరిత్రను వ్రాయడం"</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"వెబ్ బుక్మార్క్లు మరియు చరిత్రను రాయడం"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"మీ టాబ్లెట్లో నిల్వ చేయబడిన బ్రౌజర్ హిస్టరీని, బుక్మార్క్లను ఎడిట్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి యాప్ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతిని థర్డ్ పార్టీ బ్రౌజర్లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్లు అమలు చేయకపోవచ్చు."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"మీ Android TV పరికరంలో నిల్వ చేసిన బ్రౌజర్ చరిత్ర లేదా బుక్మార్క్లను సవరించడానికి యాప్ని అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను తీసివేయడానికి లేదా సవరించడానికి యాప్ని అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ-పక్ష బ్రౌజర్లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్ల ద్వారా అమలు కాకపోవచ్చు."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"మీ ఫోన్లో నిల్వ చేయబడిన బ్రౌజర్ చరిత్రను లేదా బుక్మార్క్లను సవరించడానికి యాప్ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా సవరించడానికి యాప్ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
@@ -1596,7 +1602,7 @@
<string name="data_usage_rapid_title" msgid="2950192123248740375">"అధిక మొబైల్ డేటా వినియోగం"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"మీ యాప్లు సాధారణం కంటే ఎక్కువ డేటాని ఉపయోగించాయి"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> సాధారణం కంటే ఎక్కువ డేటాని ఉపయోగించింది"</string>
- <string name="ssl_certificate" msgid="5690020361307261997">"భద్రతా సర్టిఫికెట్"</string>
+ <string name="ssl_certificate" msgid="5690020361307261997">"సెక్యూరిటీ సర్టిఫికెట్"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"ఈ సర్టిఫికెట్ చెల్లుబాటు అవుతుంది."</string>
<string name="issued_to" msgid="5975877665505297662">"దీనికి జారీ చేయబడింది:"</string>
<string name="common_name" msgid="1486334593631798443">"సాధారణ పేరు:"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 24725de..45d4222 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"แตะเพื่อลบรูปแบบใบหน้า แล้วเพิ่มใบหน้าอีกครั้ง"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ตั้งค่าการปลดล็อกด้วยใบหน้า"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ปลดล็อกโทรศัพท์โดยมองไปที่โทรศัพท์"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"หากต้องการใช้ปลดล็อกด้วยใบหน้า ให้เปิด"<b>"การเข้าถึงกล้อง"</b>"ในการตั้งค่าและความเป็นส่วนตัว"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ตั้งค่าการปลดล็อกด้วยวิธีอื่น"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"แตะเพื่อเพิ่มลายนิ้วมือ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ปลดล็อกด้วยลายนิ้วมือ"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ตรวจสอบจำนวนของรหัสผ่านที่พิมพ์ไม่ถูกต้องขณะปลดล็อกหน้าจอ และล็อกแท็บเล็ตหรือลบข้อมูลทั้งหมดในแท็บเล็ตถ้ามีการพิมพ์รหัสผ่านที่ไม่ถูกต้องมากเกินไป"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ตรวจสอบรหัสผ่านที่พิมพ์ไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกอุปกรณ์ Android TV หรือลบข้อมูลทั้งหมดของอุปกรณ์ Android TV หากมีการพิมพ์รหัสผ่านไม่ถูกต้องหลายครั้งเกินไป"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ตรวจสอบจำนวนครั้งที่พิมพ์รหัสผ่านไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกระบบสาระบันเทิงหรือลบข้อมูลทั้งหมดของระบบสาระบันเทิงหากพิมพ์รหัสผ่านไม่ถูกต้องบ่อยเกินไป"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ตรวจสอบจำนวนการพิมพ์รหัสผ่านที่ไม่ถูกต้องขณะปลดล็อกหน้าจอ และล็อกโทรศัพท์หรือลบข้อมูลทั้งหมดในโทรศัพท์ถ้ามีการพิมพ์รหัสผ่านที่ไม่ถูกต้องมากเกินไป"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ตรวจสอบจำนวนรหัสผ่านที่พิมพ์ไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกแท็บเล็ตหรือลบข้อมูลทั้งหมดของผู้ใช้นี้หากพิมพ์รหัสผ่านไม่ถูกต้องบ่อยเกินไป"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ตรวจสอบจำนวนรหัสผ่านที่พิมพ์ไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกอุปกรณ์ Android TV หรือลบข้อมูลทั้งหมดของผู้ใช้นี้หากพิมพ์รหัสผ่านไม่ถูกต้องหลายครั้งเกินไป"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ตรวจสอบจำนวนครั้งที่พิมพ์รหัสผ่านไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกระบบสาระบันเทิงหรือลบข้อมูลทั้งหมดของโปรไฟล์นี้หากพิมพ์รหัสผ่านไม่ถูกต้องบ่อยเกินไป"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ตรวจสอบจำนวนรหัสผ่านที่พิมพ์ไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกโทรศัพท์หรือลบข้อมูลทั้งหมดของผู้ใช้นี้หากพิมพ์รหัสผ่านไม่ถูกต้องบ่อยเกินไป"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"เปลี่ยนการล็อกหน้าจอ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"เปลี่ยนการล็อกหน้าจอ"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ลบข้อมูลทั้งหมด"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ลบข้อมูลของแท็บเล็ตโดยไม่มีการเตือน ด้วยการดำเนินการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ลบข้อมูลของอุปกรณ์ Android TV โดยไม่มีการเตือนด้วยการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ดำเนินการรีเซ็ตข้อมูลเป็นค่าเริ่มต้นเพื่อลบข้อมูลของระบบสาระบันเทิงโดยไม่มีการเตือน"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ลบข้อมูลโทรศัพท์โดยไม่มีการเตือน ด้วยการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ลบข้อมูลผู้ใช้"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ลบข้อมูลโปรไฟล์"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ลบข้อมูลผู้ใช้"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ลบข้อมูลของผู้ใช้นี้ในแท็บเล็ตเครื่องนี้โดยไม่มีการเตือน"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ลบข้อมูลของผู้ใช้นี้ในอุปกรณ์ Android TV เครื่องนี้โดยไม่มีการเตือน"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ลบข้อมูลของโปรไฟล์ดังกล่าวในระบบสาระบันเทิงนี้โดยไม่มีการเตือน"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ลบข้อมูลของผู้ใช้นี้ในโทรศัพท์เครื่องนี้โดยไม่มีการเตือน"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ตั้งค่าพร็อกซีส่วนกลางของอุปกรณ์"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ตั้งค่าพร็อกซีส่วนกลางของอุปกรณ์ที่จะใช้ขณะที่เปิดใช้นโยบายอยู่ เฉพาะเจ้าของอุปกรณ์เท่านั้นที่สามารถตั้งค่าพร็อกซีส่วนกลาง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index c85977a..9f994f7 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"I-tap para i-delete ang iyong face model, pagkatapos ay idagdag ulit ang mukha mo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"I-set up ang Pag-unlock Gamit ang Mukha"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"I-unlock ang iyong telepono sa pamamagitan ng pagtingin dito"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para magamit ang Pag-unlock Gamit ang Mukha, i-on ang "<b>"Access sa camera"</b>" sa Mga Setting > Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Mag-set up ng higit pang paraan para mag-unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"I-tap para magdagdag ng fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Pag-unlock Gamit ang Fingerprint"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Subaybayan ang bilang ng mga hindi tamang password na na-type kapag ina-unlock ang screen, at i-lock ang tablet o burahin ang lahat ng data ng tablet kung masyadong maraming hindi tamang password ang na-type."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Subaybayan ang dami ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang Android TV device o burahin ang lahat ng data ng Android TV device kung masyadong maraming maling password ang na-type."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Subaybayan ang bilang ng mga maling password kapag ina-unlock ang screen, at i-lock ang infotainment system o burahin ang lahat ng data ng infotainment system kung masyadong maraming maling password ang nata-type."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang telepono o burahin ang lahat ng data ng telepono kung masyadong maraming maling password ang na-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang tablet o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang nata-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Subaybayan ang dami ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang iyong Android TV device o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang na-type."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang infotainment system o burahin ang lahat ng data ng profile na ito kung masyadong maraming maling password ang nata-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang telepono o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang nata-type."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Palitan ang screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Palitan ang screen lock."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Burahin ang lahat ng data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Burahin ang data ng tablet nang walang babala sa pamamagitan ng pagsasagawa ng pag-reset ng factory data."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Burahin ang data ng iyong Android TV device nang walang babala sa pamamagitan ng pagsasagawa ng pag-reset ng factory data."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Burahin ang data ng infotainment system nang walang babala sa pamamagitan ng pagsasagawa ng pag-reset ng factory data."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Burahin ang data ng telepono nang walang babala sa pamamagitan ng pagsasagawa ng pag-reset ng factory data."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Burahin ang data ng user"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Burahin ang data ng profile"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Burahin ang data ng user"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Burahin ang data ng user na ito sa tablet na ito nang walang babala."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Burahin ang data ng user na ito sa Android TV device na ito nang walang babala."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Burahin ang data ng profile na ito sa infotainment system na ito nang walang babala."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Burahin ang data ng user na ito sa teleponong ito nang walang babala."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Itakda ang pandaigdigang proxy ng device"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Itakda ang pandaigdigang proxy ng device na gagamitin habang naka-enable ang patakaran. Ang may-ari ng device lang ang makakapagtakda sa pandaigdigang proxy."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index c85b7f6..6241427 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Yüz modelinizi silmek için dokunup ardından yüzünüzü yeniden ekleyin"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Yüz Tanıma Kilidi\'ni kurma"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonunuza bakarak kilidini açın"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Yüz Tanıma Kilidi\'ni kullanmak için Ayarlar > Gizlilik bölümünden "<b>"Kamera erişimi"</b>"\'ni açın"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kilidi açmak için daha fazla yöntem ayarlayın"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Parmak izi eklemek için dokunun"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Parmak İzi Kilidi"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekran kilidini açma denemelerini izle"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Ekran kilidini açarken yapılan yanlış şifre girme denemelerini izler ve çok fazla sayıda yanlış şifre girme denemesi yapılmışsa tableti kilitler veya tabletteki tüm verileri siler."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip eder ve çok fazla sayıda hatalı şifre girildiğinde Android TV cihazınızı kilitler veya Android TV cihazınızın tüm verilerini siler."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Ekran kilidi açılırken girilen hatalı şifre sayısı takip edilir ve çok fazla sayıda hatalı şifre girildiğinde bilgi-eğlence sistemi kilitlenir ya da bilgi-eğlence sistemindeki tüm veriler silinir."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Ekran kilidini açarken yapılan yanlış şifre girişi denemelerini izler ve çok sayıda yanlış şifre girişi denemesi yapılmışsa telefonu kilitler veya telefonun tüm verilerini siler."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip edin ve çok fazla sayıda hatalı şifre girildiğinde tableti kilitleyin veya söz konusu kullanıcının tüm verilerini silin."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip edin ve çok fazla sayıda hatalı şifre girildiğinde Android TV cihazınızı kilitleyin veya söz konusu kullanıcının tüm verilerini silin."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Ekran kilidi açılırken girilen hatalı şifre sayısı takip edilir ve çok fazla sayıda hatalı şifre girildiğinde tablet kilitlenir veya söz konusu kullanıcının tüm verileri silinir."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip edin ve çok fazla sayıda hatalı şifre girildiğinde telefonu kilitleyin veya söz konusu kullanıcının tüm verilerini silin."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ekran kilidini değiştirme"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ekran kilidini değiştirir."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Tüm verileri silme"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek tabletteki verileri uyarıda bulunmadan siler."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek Android TV cihazınızdaki verileri uyarıda bulunmadan siler."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek bilgi-eğlence sistemindeki veriler uyarıda bulunmadan silinir."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek telefondaki verileri uyarıda bulunmadan siler."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Kullanıcı verilerini sil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profil verilerini silme"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Kullanıcı verilerini sil"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Uyarı yapmadan bu kullanıcının bu tabletteki verilerini silin."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Uyarı yapmadan bu kullanıcının bu Android TV cihazındaki verilerini siler."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bu bilgi-eğlence sistemindeki bu profilin verileri uyarıda bulunmadan silinir."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Uyarı yapmadan bu kullanıcının bu telefondaki verilerini siler."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Cihaz genelinde geçerli proxy\'i ayarla"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Politika etkin olduğunda kullanılacak cihaz genelinde geçerli proxy\'yi ayarlar. Genel proxy\'yi yalnızca cihaz sahibi ayarlayabilir."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 8ff65f1..679cddc 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -622,6 +622,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Натисніть, щоб видалити свою модель обличчя, а потім знову додайте її"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Налаштування фейсконтролю"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ви зможете розблоковувати телефон, подивившись на нього"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Щоб використовувати фейсконтроль, увімкніть "<b>"Доступ до камери"</b>" в розділі \"Налаштування\" > \"Конфіденційність\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Налаштуйте більше способів розблокування"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Натисніть, щоб додати відбиток пальця"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Розблокування відбитком пальця"</string>
@@ -735,9 +736,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Відстежувати спроби розблокування екрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Відстежувати кількість неправильних паролів, введених під час розблокування екрана, і блокувати планшетний ПК або стирати всі його дані, якщо введено забагато неправильних паролів."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана. Блокуйте пристрій Android TV або стирайте всі його дані, якщо пароль введено неправильно забагато разів."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана, і блокуйте інформаційно-розважальну систему або видаляйте всі її дані, якщо пароль введено неправильно забагато разів."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Відстежувати кількість неправильних паролів, введених під час розблокування екрана, і блокувати телефон або стирати всі його дані, якщо введено забагато неправильних паролів."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана. Блокуйте планшет або стирайте всі його дані, якщо пароль введено неправильно забагато разів."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана. Блокуйте пристрій Android TV або стирайте всі дані користувача, якщо пароль введено неправильно забагато разів."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана, і блокуйте інформаційно-розважальну систему або видаляйте всі дані цього профілю, якщо пароль введено неправильно забагато разів."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана. Блокуйте телефон або стирайте всі його дані, якщо пароль введено неправильно забагато разів."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Змінити спосіб розблокування екрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Змінити спосіб розблокування екрана."</string>
@@ -746,10 +749,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Видалити всі дані"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Стирати дані планшетного ПК без попередження, відновлюючи заводські налаштування."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Видаляйте дані пристрою Android TV без попередження шляхом відновлення заводських налаштувань."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Видаляйте всі дані інформаційно-розважальної системи без попередження, відновлюючи заводські налаштування."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Стирати дані телефона без попередження, відновивши заводські налаштування."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Видалення даних користувача"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Видалити всі дані профілю"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Видалення даних користувача"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Видаляйте дані користувача на цьому планшеті без попередження."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Видаляйте дані користувача на цьому пристрої Android TV без попередження."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Видаляйте всі дані цієї інформаційно-розважальної системи без попередження."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Видаляйте дані користувача на цьому телефоні без попередження."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Установ. глоб. проксі пристрою"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Використовуйте глобальний проксі-сервер пристрою, коли це правило ввімкнено. Налаштувати глобальний проксі-сервер може лише власник пристрою."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index a189441..12d454e 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"اپنے چہرے کا ماڈل حذف کرنے کے لیے تھپتھپائیں پھر اپنا چہرہ دوبارہ شامل کریں"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"فیس اَنلاک سیٹ اپ کریں"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"اپنے فون کی طرف دیکھ کر اسے غیر مقفل کریں"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"فیس اَنلاک کا استعمال کرنے کے لیے، ترتیبات اور رازداری میں "<b>"کیمرے تک رسائی"</b>" کو آن کریں"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"غیر مقفل کرنے کے مزید طریقے سیٹ اپ کریں"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"فنگر پرنٹ شامل کرنے کیلئے تھپتھپائیں"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فنگر پرنٹ اَن لاک"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"اسکرین غیر مقفل کرنے کی کوششیں مانیٹر کریں"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کیے گئے غلط پاس ورڈز کی تعداد مانیٹر کریں اور ٹیبلیٹ کو مقفل کریں یا اگر کافی زیادہ غلط پاس ورڈز ٹائپ کیے گئے ہیں تو ٹیبلیٹ کا سبھی ڈیٹا صاف کریں۔"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد پر نگاہ رکھیں اور اگر بہت زیادہ غلط پاسورڈز ٹائپ کیے جاتے ہیں تو اپنے Android TV آلہ کو مقفل کردیں یا اپنے Android TV آلہ کے ڈیٹا کو مٹادیں۔"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کیے گئے غلط پاس ورڈز کی تعداد مانیٹر کریں۔ اور معلوماتی انٹرٹینمنٹ سسٹم کو مقفل کریں یا اگر کافی زیادہ غلط پاس ورڈز ٹائپ کیے گئے ہیں تو معلوماتی انٹرٹینمنٹ سسٹم کا سبھی ڈیٹا صاف کریں۔"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کیے گئے غلط پاس ورڈز کی تعداد مانیٹر کریں اور فون کو مقفل کریں یا اگر کافی زیادہ غلط پاس ورڈز ٹائپ کیے گئے ہیں تو فون کا سبھی ڈیٹا صاف کریں۔"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد پر نگاہ رکھیں اور اگر بہت زیادہ غلط پاس ورڈز ٹائپ کیے جاتے ہیں تو ٹیبلٹ کو مقفل کریں یا اس صارف کا سبھی ڈیٹا ہٹائیں۔"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد پر نگاہ رکھیں اور اگر بہت زیادہ غلط پاس ورڈز ٹائپ کیے جاتے ہیں تو اپنے Android TV آلہ کو مقفل کریں یا اس صارف کا سبھی ڈیٹا ہٹائیں۔"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد مانیٹر کریں اور معلوماتی انٹرٹینمنٹ سسٹم کو مقفل کریں اگر بہت زیادہ غلط پاس ورڈز ٹائپ کیے جاتے ہیں تو اس پروفائل کا سبھی ڈیٹا ہٹائیں۔"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد پر نگاہ رکھیں اور اگر بہت زیادہ غلط پاس ورڈز ٹائپ کیے جاتے ہیں تو فون کو مقفل کریں یا اس صارف کا سبھی ڈیٹا ہٹائیں۔"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"اسکرین لاک تبدیل کریں"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"اسکرین لاک تبدیل کریں۔"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"سبھی ڈیٹا صاف کریں"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"فیکٹری ڈیٹا ری سیٹ انجام دے کر وارننگ کے بغیر ٹیبلٹ کا ڈیٹا مٹائیں۔"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"فیکٹری ڈیٹا ری سیٹ کو انجام دے کر انتباہ کیے بغیر اپنے Android TV آلہ کا ڈیٹا مٹائیں۔"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"فیکٹری ڈیٹا ری سیٹ کو انجام دے کر وارننگ کے بغیر معلوماتی انٹرٹینمنٹ سسٹم کا ڈیٹا صاف کریں۔"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"فیکٹری ڈیٹا ری سیٹ انجام دے کر وارننگ کے بغیر فون کا ڈیٹا مٹائیں۔"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"صارف کا ڈیٹا ہٹائیں"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"پروفائل ڈیٹا صاف کریں"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"صارف کا ڈیٹا ہٹائیں"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"وارننگ کے بغیر اس ٹیبلٹ پر موجود اس صارف کا ڈیٹا ہٹائیں۔"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"انتباہ کے بغیر اس Android TV آلہ پر اس صارف کا ڈیٹا ہٹائیں۔"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"وارننگ کے بغیر اس معلوماتی انٹرٹینمنٹ سسٹم پر اس پروفائل کا ڈیٹا صاف کریں۔"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"وارننگ کے بغیر اس فون پر موجود اس صارف کا ڈیٹا ہٹائیں۔"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"آلہ کی عالمی پراکسی سیٹ کریں"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"پالیسی فعال ہونے پر آلہ کی عالمی پراکسی کو استعمال کیے جانے کیلئے سیٹ کریں۔ صرف آلہ کا مالک ہی عالمی پراکسی سیٹ کر سکتا ہے۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 624cf74..03b4fb8 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Yuz modelini oʻchirish uchun bosing va keyin yana yuzni qoʻshing"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Yuz bilan ochishni sozlash"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefoningizni yuz tekshiruvi yordamida qulfdan chiqaring"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Yuz bilan ochish uchun Sozlamalar va maxfiylik orqali "<b>"kameraga kirishga ruxsat bering"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Qulfdan chiqarishning boshqa usullarini sozlang"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Barmoq izi kiritish uchun bosing"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Barmoq izi bilan ochish"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranni qulfdan chiqarishga urinishlarni nazorat qilish"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Ekranni qulfini ochishda parolni kiritishga urinishlarni kuzatib boradi va agar parol bir necha marta noto‘g‘ri kiritilsa, planshetni qulflaydi yoki undagi ma’lumotlarni o‘chirib tashlaydi."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Ekran qulfini ochishda kiritilgan xato parollar sonini kuzatib boradi va agar parol juda koʻp marta xato kiritilsa, Android TV qurilmasini qulflaydi yoki undagi barcha maʼlumotlarni oʻchirib tashlaydi."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Ekranni qulfini ochishda parolni kiritishga urinishlarni kuzatib boradi va agar parol bir necha marta xato kiritilsa, axborot-hordiq tizimini qulflaydi yoki undagi maʼlumotlarni oʻchirib tashlaydi."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Ekranni qulfini ochishda parolni kiritishga urinishlarni kuzatib boradi va agar parol bir necha marta noto‘g‘ri kiritilsa, telefonni qulflaydi yoki undagi ma’lumotlarni o‘chirib tashlaydi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Ekran qulfini ochishda kiritilgan noto‘g‘ri parollar sonini kuzatib boradi va agar parol juda ko‘p marta noto‘g‘ri kiritilsa, planshetni qulflaydi yoki undagi barcha ma’lumotlarni o‘chirib tashlaydi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Ekran qulfini ochishda kiritilgan xato parollar sonini kuzatib boradi va agar parol juda koʻp marta xato kiritilsa, Android TV qurilmangizni qulflaydi yoki undagi barcha maʼlumotlarni oʻchirib tashlaydi."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Ekran qulfini ochishda parolni kiritishga urinishlarni kuzatib boradi va agar parol bir necha marta xato kiritilsa, axborot-hordiq tizimini qulflaydi yoki undagi barcha profil maʼlumotlarini oʻchirib tashlaydi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Ekran qulfini ochishda kiritilgan noto‘g‘ri parollar sonini kuzatib boradi va agar parol juda ko‘p marta noto‘g‘ri kiritilsa, telefonni qulflaydi yoki undagi barcha ma’lumotlarni o‘chirib tashlaydi."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ekran qulfini almashtirish"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ekran qulfini almashtiradi."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Hamma narsani tozalash"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Planshetdagi hamma narsani tozalab tashlaydi va uning sozlamalarini asliga qaytaradi."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Android TV qurilmangizdagi hamma narsani tozalab tashlaydi va uning sozlamalarini asliga qaytaradi."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Zavod sozlamalarini qayta tiklash orqali axbirot-hordiq tizimi maʼlumotlarini ogohlantirishsiz oʻchirib tashlansin."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Telefondagi hamma narsani tozalab tashlaydi va uning sozlamalarini asliga qaytaradi."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Foydalanuvchi ma’lumotlarini o‘chirish"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profil maʼlumotlarini tozalash"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Foydalanuvchi ma’lumotlarini o‘chirish"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ushbu planshetdagi foydalanuvchi ma’lumotlarini ogohlantirishsiz o‘chirib tashlaydi."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Bu Android TV qurilmangizdagi foydalanuvchi maʼlumotlarini ogohlantirishsiz oʻchirib tashlaydi."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bu profil maʼlumotlari ogohlantirishsiz oʻchirib tashlansin."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ushbu telefondagi foydalanuvchi ma’lumotlarini ogohlantirishsiz o‘chirib tashlaydi."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Qurilmaga global proksi o‘rnatish"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Qoida faollashtirilgan vaqtda ishlatiladigan qurilmaning global proksi-serverini o‘rnatadi. Faqat qurilma egasi global proksi-serverini o‘rnatishi mumkin."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4ae1a17..ba40d44 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Nhấn để xóa mẫu khuôn mặt, sau đó thêm lại khuôn mặt của bạn"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Thiết lập tính năng Mở khóa bằng khuôn mặt"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mở khóa điện thoại bằng cách nhìn vào điện thoại"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Để dùng tính năng Mở khoá bằng khuôn mặt, hãy bật tuỳ chọn "<b>"Truy cập máy ảnh"</b>" trong phần Cài đặt > Quyền riêng tư"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Thiết lập thêm những cách mở khóa khác"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Nhấn để thêm vân tay"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Mở khóa bằng vân tay"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Giám sát những lần thử mở khóa màn hình"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Theo dõi số lần nhập mật khẩu không chính xác khi mở khóa màn hình và khóa máy tính bảng hoặc xóa tất cả dữ liệu của máy tính bảng nếu có quá nhiều lần nhập mật khẩu không chính xác."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Giám sát số lần nhập mật khẩu không chính xác khi mở khóa màn hình, đồng thời khóa thiết bị Android TV hoặc xóa tất cả dữ liệu trên thiết bị Android TV nếu bạn nhập mật khẩu sai quá nhiều lần."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Theo dõi số lần nhập sai mật khẩu khi mở khoá màn hình và khoá hệ thống thông tin giải trí hoặc xoá tất cả dữ liệu của hệ thống này nếu có quá nhiều lần nhập sai mật khẩu."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Theo dõi số lần nhập mật khẩu không chính xác khi mở khóa màn hình và khóa điện thoại hoặc xóa tất cả dữ liệu của điện thoại nếu có quá nhiều lần nhập mật khẩu không chính xác."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Giám sát số lần nhập sai mật khẩu khi mở khóa màn hình và khóa máy tính bảng hoặc xóa tất cả dữ liệu của người dùng này nếu nhập sai mật khẩu quá nhiều lần."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Giám sát số lần nhập mật khẩu không chính xác khi mở khóa màn hình, đồng thời khóa thiết bị Android TV hoặc xóa tất cả dữ liệu của người dùng này nếu bạn nhập mật khẩu sai quá nhiều lần."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Theo dõi số lần nhập sai mật khẩu khi mở khoá màn hình và khoá hệ thống thông tin giải trí hoặc xoá tất cả dữ liệu của hồ sơ này nếu có quá nhiều lần nhập sai mật khẩu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Giám sát số lần nhập sai mật khẩu khi mở khóa màn hình và khóa điện thoại hoặc xóa tất cả dữ liệu của người dùng này nếu nhập sai mật khẩu quá nhiều lần."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Thay đổi phương thức khóa màn hình"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Thay đổi phương thức khóa màn hình."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Xóa tất cả dữ liệu"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Xóa dữ liệu trên máy tính bảng mà không cần cảnh báo, bằng cách thực hiện thiết lập lại dữ liệu ban đầu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Xóa dữ liệu trên thiết bị Android TV mà không cảnh báo bằng cách thiết lập lại dữ liệu ban đầu."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Xoá dữ liệu của hệ thống thông tin giải trí mà không cảnh báo bằng cách đặt lại dữ liệu về trạng thái ban đầu."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Xóa dữ liệu trên điện thoại mà không cần cảnh báo, bằng cách thiết lập lại dữ liệu ban đầu."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Xóa dữ liệu người dùng"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Xoá dữ liệu hồ sơ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Xóa dữ liệu người dùng"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Xóa dữ liệu của người dùng trên máy tính bảng này mà không cảnh báo."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Xóa dữ liệu của người dùng này trên thiết bị Android TV mà không cảnh báo."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Xoá dữ liệu của hồ sơ này trên hệ thống thông tin giải trí này mà không cảnh báo."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Xóa dữ liệu của người dùng trên điện thoại này mà không cảnh báo."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Đặt proxy chung của điện thoại"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Đặt proxy chung của thiết bị được sử dụng trong khi chính sách bật. Chỉ chủ sở hữu thiết bị mới có thể đặt proxy chung."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d870764..132cabd 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"请点按以删除您的脸部模型,然后再添加您的脸部模型"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"设置人脸解锁"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"脸部对准手机即可将其解锁"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如需使用人脸解锁功能,请在“设置”>“隐私权”中开启"<b>"摄像头使用权限"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"设置更多解锁方式"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"点按即可添加指纹"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指纹解锁"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"监控屏幕解锁尝试次数"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"监视在解锁屏幕时输错密码的次数,如果输错次数过多,则锁定平板电脑或清除其所有数据。"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"监控用户在解锁屏幕时输错密码的次数;如果用户输错密码的次数超出上限,系统就会锁定 Android TV 设备或清空 Android TV 设备上的所有数据。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定信息娱乐系统或清除信息娱乐系统上的所有数据。"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"监视在解锁屏幕时输错密码的次数,如果输错次数过多,则锁定手机或清除其所有数据。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定平板电脑或清空此用户的所有数据。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"监控用户在解锁屏幕时输错密码的次数;如果用户输错密码的次数超出上限,系统就会锁定 Android TV 设备或清空该用户的所有数据。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定信息娱乐系统或清除此个人资料的所有数据。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定手机或清空此用户的所有数据。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"更改锁屏方式"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"更改锁屏方式。"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"清除所有数据"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"恢复出厂设置时,系统会在不发出警告的情况下清除平板电脑上的数据。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"不事先发出警告就以恢复出厂设置的方式清空 Android TV 设备中的数据。"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"在不发出警告的情况下,通过恢复出厂设置来清除信息娱乐系统上的数据。"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"恢复出厂设置时,系统会在不发出警告的情况下清除手机上的数据。"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"清空用户数据"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"清除个人资料数据"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"清空用户数据"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"清空此用户在这台平板电脑上的数据,而不事先发出警告。"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"不事先发出警告就清空此用户在这台 Android TV 设备上的数据。"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"在不发出警告的情况下,从该信息娱乐系统中清除此个人资料的所有数据。"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"清空此用户在这部手机上的数据,而不事先发出警告。"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"设置设备全局代理"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"设置在规范启用时要使用的设备全局代理。只有设备所有者才能设置全局代理。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index deae9a3..0f81d75 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"請輕按這裡刪除面部模型,然後再重新新增"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"設定「面孔解鎖」"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"直望手機即可解鎖"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如要使用「面孔解鎖」,請在 [設定] > [私隱] 開啟"<b>"相機存取權"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方法"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"輕按即可新增指紋"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋解鎖"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"監視為螢幕解鎖時輸入錯誤密碼的次數;如果輸入錯誤密碼的次數過多,則會鎖定平板電腦或清除平板電腦的所有資料。"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"監察螢幕解鎖時錯誤輸入密碼的次數,並在錯誤輸入密碼的次數過多時,將 Android TV 裝置上鎖或清除裝置上的所有資料。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"監察螢幕解鎖時錯誤輸入密碼的次數,如果錯誤輸入密碼的次數過多,即鎖定資訊娛樂系統或清除資訊娛樂系統中的所有資料。"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"監視為螢幕解鎖時輸入錯誤密碼的次數,如果輸入錯誤密碼的次數過多,則會鎖定手機或清除手機的所有資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"監察螢幕解鎖時錯誤輸入密碼的次數,如果錯誤輸入密碼的次數過多,即鎖定平板電腦或清除這個使用者的資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"監察螢幕解鎖密碼輸入錯誤的次數,並在密碼輸入錯誤的次數超出上限時將 Android TV 裝置上鎖,或清除該使用者的所有資料。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"監察螢幕解鎖時錯誤輸入密碼的次數,如果錯誤輸入密碼的次數過多,即鎖定資訊娛樂系統或清除這個設定檔的所有資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"監察螢幕解鎖時錯誤輸入密碼的次數,如果錯誤輸入密碼的次數過多,即鎖定手機或清除這個使用者的資料。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"變更螢幕鎖定"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"變更螢幕鎖定。"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"清除所有資料"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"重設平板電腦為原廠設定,在不提出警告的情況下直接清除平板電腦的資料。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"系統可將 Android TV 裝置回復原廠設定,在沒有警告的情況下清除裝置的資料。"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"回復資訊娛樂系統為原廠設定,在不提出警告的情況下直接清除資訊娛樂系統的資料。"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"重設手機為原廠設定,在不提出警告的情況下直接清除手機的資料。"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"清除使用者資料"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"清除個人檔案資料"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"清除使用者資料"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"清除這個使用者在這部平板電腦上的資料而不作警告。"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"在沒有警告的情況下,清除這位使用者在此 Android TV 裝置上的資料。"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"在不提出警告的情況下直接清除此設定檔在資訊娛樂系統上的資料。"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"清除這個使用者在這部手機上的資料而不作警告。"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"設定裝置的全域代理伺服器"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"設定政策啟用時所要使用的裝置全域代理伺服器,只有裝置擁有者可以設定全域代理伺服器。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f621a67..c427708 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"請輕觸這裡刪除臉部模型,然後再重新新增"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"設定人臉解鎖功能"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"看著手機就能解鎖"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如要使用人臉解鎖功能,請前往「設定」>「隱私權」開啟"<b>"攝影機存取權"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方式"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"輕觸即可新增指紋"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋解鎖"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"監控螢幕解鎖時密碼輸入錯誤的次數;如果密碼輸入錯誤的次數過多,則會鎖住平板電腦或全部清除平板電腦中的資料。"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"監控螢幕解鎖密碼輸入錯誤的次數。如果輸入錯誤的次數超過上限,系統會將 Android TV 裝置鎖定,或清除 Android TV 裝置的所有資料。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"監控密碼輸入錯誤的次數。解鎖螢幕時,如果密碼輸入錯誤次數過多,系統就會鎖住資訊娛樂系統或清除資訊娛樂系統的所有資料。"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"監控螢幕解鎖時密碼輸入錯誤的次數;如果密碼輸入錯誤的次數過多,則會鎖住手機或清除手機的所有資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"監控螢幕解鎖密碼輸入錯誤的次數;如果輸入錯誤的次數超過上限,系統會將平板電腦鎖定,或將這個使用者的資料全部清除。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"監控螢幕解鎖密碼輸入錯誤的次數。如果輸入錯誤的次數超過上限,系統會將 Android TV 裝置鎖定,或清除這位使用者的所有資料。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"監控螢幕解鎖時的密碼輸入錯誤次數。如果錯誤次數過多,系統就會鎖住資訊娛樂系統或清除這個設定檔的所有資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"監控螢幕解鎖密碼輸入錯誤的次數;如果輸入錯誤的次數超過上限,系統會將手機鎖定,或將這個使用者的資料全部清除。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"變更鎖定螢幕方式"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"變更鎖定螢幕方式。"</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"清除所有資料"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"恢復原廠設定,不提出警告就直接清除平板電腦的資料。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"在沒有事先警告的情況下讓系統恢復原廠設定,清除 Android TV 裝置上的資料。"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"不事先警告就透過恢復原廠設定的方式清除資訊娛樂系統的資料。"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"恢復原廠設定,不提出警告就直接清除手機的資料。"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"清除使用者資料"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"清除設定檔資料"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"清除使用者資料"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"將這個使用者的資料從這台平板電腦中清除,而不事先發出警告。"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"在沒有事先警告的情況下,將這位使用者的資料從這部 Android TV 裝置中清除。"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"不事先警告就從這個資訊娛樂系統上清除這個設定檔的資料。"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"將這個使用者的資料從這支手機中清除,而不事先發出警告。"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"設定裝置全域 Proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"設定政策啟用時要使用的裝置全域 Proxy。只有裝置擁有者可以設定全域 Proxy。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a193ef0..b05c2da 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -616,6 +616,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Thepha ukuze usule imodeli yakho yobuso, bese wengeza futhi ubuso"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Setha Ukuvula ngobuso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Vula ifoni yakho ngokuyibheka"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ukuze usebenzise Ukuvula ngobuso, vula "<b>"Ukufinyelela kwekhamera"</b>" kokuthi Amasethingi > Ubumfihlo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Setha izindlela eziningi zokuvula"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Thepha ukuze ungeze izigxivizo zomunwe"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ukuvula ngesigxivizo somunwe"</string>
@@ -729,9 +730,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Qapha imizamo yokuvula isikrini sakho"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Bheka inani lamaphasiwedi angafanele athayishiwe uma kuvulwa iskrini bese kuvalwa ithebhulethi noma kususwe yonke idatha yethebhulethi uma kubhalwe amaphasiwedi amaningi angalungile."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Ngamela inombolo yamaphasiwedi angalungile athayiphiwe uma kuvulwa isikrini, nokukhiya idivayisi yakho ye-Android TV noma ukususa yonke idatha yedivayisi yakho ye-Android TV uma amaphasiwedi amaningi angalungile athayiphiwe."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Qaphela inombolo yamaphasiwedi angalungile athayiphiwe uma kuvulwa isikrini, uphinde ukhiye isistimu ye-infotainment noma usule yonke idatha yesistimu ye-infotainment uma ngabe kuthayiphwa amaphasiwedi amaningi angalungile."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Bheka isibalo samaphasiwedi ngalungile afakiwe uma uvula iskrini bese uvala ucingo noma ususe yonke imininingwane yocingo uma kubhalwe amaphasiwedi amaningi angalungile."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Qaphela inombolo yamaphasiwedi angalungile athayiphwe uma kuvulwa isikrini, uphinde ukhiye ithebulethi noma usule yonke idatha yalo msebenzisi uma ngabe kuthayiphwe amaphasiwedi amaningi kakhulu angalungile."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Qaphela inombolo yamaphasiwedi angalungile athayiphiwe uma kuvulwa isikrini, uphinde ukhiye idivayisi yakho ye-Android TV noma usule yonke idatha yalo msebenzisi uma ngabe kuthayiphwe amaphasiwedi amaningi kakhulu angalungile."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Qaphela inombolo yamaphasiwedi angalungile athayiphwe uma kuvulwa isikrini, uphinde ukhiye isistimu ye-infotainment noma usule yonke idatha yaleli phrofayela uma kuthayiphwe amaphasiwedi amaningi kakhulu angalungile."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Qaphela inombolo yamaphasiwedi angalungile athayiphiwe uma kuvulwa isikrini, uphinde ukhiye ifoni noma usule yonke idatha yalo msebenzisi uma ngabe kuthayiphwe amaphasiwedi amaningi kakhulu angalungile."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Guqula ukukhiya isikrini"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Guqula ukukhiya isikrini."</string>
@@ -740,10 +743,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Sula yonke idatha"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Sula idatha yethebhulethi ngaphandle kwesaziso, ngokwenza ukusetha kabusha kwemboni."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Sula idatha yakho yedivayisi ye-Android TV ngaphandle kokuxwayisa ngokwenza ukusetha kwedatha kwefekthri."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Sula idatha isistimu ye-infotainment ngaphandle kokuxwayisa ngokwenza ukusetha kabusha kwasekuqaleni kwedatha."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Sula idatha yefoni ngaphandle kwesixwayiso, ngokwenza ukuhlela kabusha idatha yemboni"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Sula idatha yomsebenzisi"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Sula idatha yephrofayela"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Sula idatha yomsebenzisi"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Sula idatha yalo msebenzisi kule thebulethi ngaphandle kwesexwayiso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Sula le datha yomsebenzisi kule divayisi ye-Android TV ngaphandle kwesexwayiso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Sula idatha yale phrofayela kule sistimu ye-infotainment ngaphandle kwesixwayiso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Sula idatha yalo msebenzisi kule foni ngaphandle kwesexwayiso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Misa ummelelii jikelele yedivaysi"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Setha ummeleli womhlaba wonke wedivayisi ozosetshenziswa ngenkathi inqubomgomo inikwe amandla. Ngumnikazi wedivayisi kuphela ongasetha ummeleli womhlaba wonke."</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5aff0e0..7bedcc6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4879,9 +4879,10 @@
the app in the letterbox mode. -->
<item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item>
- <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
- min between device bottom corner radii will be used instead. -->
- <integer name="config_letterboxActivityCornersRadius">-1</integer>
+ <!-- Corners radius for activity presented the letterbox mode. Values < 0 enable rounded
+ corners with radius equal to min between device bottom corner radii. Default 0 value turns
+ off rounded corners logic in LetterboxUiController. -->
+ <integer name="config_letterboxActivityCornersRadius">0</integer>
<!-- Blur radius for the Option 3 in R.integer.config_letterboxBackgroundType. Values < 0 are
ignored and 0 is used. -->
@@ -4937,6 +4938,10 @@
If given value is outside of this range, the option 1 (center) is assummed. -->
<integer name="config_letterboxDefaultPositionForReachability">1</integer>
+ <!-- Whether a camera compat controller is enabled to allow the user to apply or revert
+ treatment for stretched issues in camera viewfinder. -->
+ <bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -5283,4 +5288,6 @@
<string name="config_work_badge_path_24" translatable="false">
M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z
</string>
+
+ <integer name="config_chooser_max_targets_per_row">4</integer>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0706d8a..f331f1a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -900,7 +900,8 @@
<dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
<!-- chooser/resolver (sharesheet) spacing -->
- <dimen name="chooser_corner_radius">16dp</dimen>
+ <dimen name="chooser_width">412dp</dimen>
+ <dimen name="chooser_corner_radius">28dp</dimen>
<dimen name="chooser_row_text_option_translate">25dp</dimen>
<dimen name="chooser_view_spacing">18dp</dimen>
<dimen name="chooser_edge_margin_thin">16dp</dimen>
@@ -917,7 +918,7 @@
<dimen name="resolver_icon_size">32dp</dimen>
<dimen name="resolver_button_bar_spacing">0dp</dimen>
<dimen name="resolver_badge_size">18dp</dimen>
- <dimen name="resolver_icon_margin">16dp</dimen>
+ <dimen name="resolver_icon_margin">8dp</dimen>
<dimen name="resolver_small_margin">18dp</dimen>
<dimen name="resolver_edge_margin">24dp</dimen>
<dimen name="resolver_elevation">1dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3c7f026..166d6ab 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1666,6 +1666,8 @@
<string name="face_setup_notification_title">Set up Face Unlock</string>
<!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
<string name="face_setup_notification_content">Unlock your phone by looking at it</string>
+ <!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] -->
+ <string name="face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string>
<!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
<string name="fingerprint_setup_notification_title">Set up more ways to unlock</string>
<!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a23816e..46b249e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -275,6 +275,7 @@
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
<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" />
<java-symbol type="bool" name="config_flipToScreenOffEnabled" />
<java-symbol type="integer" name="config_flipToScreenOffMaxLatencyMicros" />
<java-symbol type="bool" name="config_bluetooth_sco_off_call" />
@@ -2570,6 +2571,7 @@
<java-symbol type="string" name="face_recalibrate_notification_name" />
<java-symbol type="string" name="face_recalibrate_notification_title" />
<java-symbol type="string" name="face_recalibrate_notification_content" />
+ <java-symbol type="string" name="face_sensor_privacy_enabled" />
<java-symbol type="string" name="face_error_unable_to_process" />
<java-symbol type="string" name="face_error_hw_not_available" />
<java-symbol type="string" name="face_error_no_space" />
@@ -2843,6 +2845,7 @@
<java-symbol type="layout" name="date_picker_month_item_material" />
<java-symbol type="id" name="month_view" />
<java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
+ <java-symbol type="dimen" name="chooser_width" />
<java-symbol type="dimen" name="chooser_corner_radius" />
<java-symbol type="string" name="chooser_no_direct_share_targets" />
<java-symbol type="drawable" name="chooser_row_layer_list" />
@@ -4270,6 +4273,7 @@
<java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
<java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" />
<java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" />
+ <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/core/tests/coretests/res/drawable/custom_drawable.xml
similarity index 70%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to core/tests/coretests/res/drawable/custom_drawable.xml
index 9f44aca..ebb821f 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/core/tests/coretests/res/drawable/custom_drawable.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -14,7 +14,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
+
+<drawable xmlns:android="http://schemas.android.com/apk/res/android"
+ class="android.window.CustomDrawable"
+ android:drawable="@drawable/bitmap_drawable"
+ android:inset="10dp" />
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index df0c64c..9f48c06 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -532,7 +532,7 @@
}
@Override
- public void scheduleCrash(String s, int i) throws RemoteException {
+ public void scheduleCrash(String s, int i, Bundle extras) throws RemoteException {
}
@Override
diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
index 0456029..98485c0 100644
--- a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
+++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
@@ -18,8 +18,7 @@
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import android.annotation.Nullable;
import android.platform.test.annotations.Presubmit;
@@ -146,24 +145,17 @@
private static void testNeverConstrainDisplayApis(String packageName, long version,
boolean expected) {
- boolean result = ConstrainDisplayApisConfig.neverConstrainDisplayApis(
- buildApplicationInfo(packageName, version));
- if (expected) {
- assertTrue(result);
- } else {
- assertFalse(result);
- }
+ ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
+ assertEquals(expected,
+ config.getNeverConstrainDisplayApis(buildApplicationInfo(packageName, version)));
}
private static void testAlwaysConstrainDisplayApis(String packageName, long version,
boolean expected) {
- boolean result = ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(
- buildApplicationInfo(packageName, version));
- if (expected) {
- assertTrue(result);
- } else {
- assertFalse(result);
- }
+ ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
+
+ assertEquals(expected,
+ config.getAlwaysConstrainDisplayApis(buildApplicationInfo(packageName, version)));
}
private static ApplicationInfo buildApplicationInfo(String packageName, long version) {
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index b3450de..b6a182c 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.TOOL_TYPE_FINGER;
@@ -189,4 +190,27 @@
assertEquals(950, (int) rot270.getX());
assertEquals(30, (int) rot270.getY());
}
+
+ @Test
+ public void testUsesPointerSourceByDefault() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 0 /* x */, 0 /* y */, 0 /* metaState */);
+ assertTrue(event.isFromSource(SOURCE_CLASS_POINTER));
+ }
+
+ @Test
+ public void testLocationOffsetOnlyAppliedToNonPointerSources() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 10 /* x */, 20 /* y */, 0 /* metaState */);
+ event.offsetLocation(40, 50);
+
+ // The offset should be applied since a pointer source is used by default.
+ assertEquals(50, (int) event.getX());
+ assertEquals(70, (int) event.getY());
+
+ // The offset should not be applied if the source is changed to a non-pointer source.
+ event.setSource(InputDevice.SOURCE_JOYSTICK);
+ assertEquals(10, (int) event.getX());
+ assertEquals(20, (int) event.getY());
+ }
}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/tests/coretests/src/android/window/CustomDrawable.java
similarity index 79%
rename from core/java/android/window/TaskFragmentAppearedInfo.aidl
rename to core/tests/coretests/src/android/window/CustomDrawable.java
index 3729c09..c25f877 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/tests/coretests/src/android/window/CustomDrawable.java
@@ -16,8 +16,10 @@
package android.window;
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+import android.graphics.drawable.InsetDrawable;
+
+public class CustomDrawable extends InsetDrawable {
+ public CustomDrawable() {
+ super(null, 0);
+ }
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index a6e351d..52cb9f3 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -24,16 +24,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.content.res.Configuration;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
-import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -59,17 +56,14 @@
public class WindowContextControllerTest {
private WindowContextController mController;
@Mock
- private IWindowManager mMockWms;
- @Mock
private WindowTokenClient mMockToken;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mController = new WindowContextController(mMockToken, mMockWms);
+ mController = new WindowContextController(mMockToken);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
- doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
- anyInt(), anyInt(), any());
+ doReturn(true).when(mMockToken).attachToDisplayArea(anyInt(), anyInt(), any());
}
@Test(expected = IllegalStateException.class)
@@ -81,10 +75,10 @@
}
@Test
- public void testDetachIfNeeded_NotAttachedYet_DoNothing() throws Exception {
+ public void testDetachIfNeeded_NotAttachedYet_DoNothing() {
mController.detachIfNeeded();
- verify(mMockWms, never()).detachWindowContextFromWindowContainer(any());
+ verify(mMockToken, never()).detachFromWindowContainerIfNeeded();
}
@Test
@@ -93,8 +87,6 @@
null /* options */);
assertThat(mController.mAttachedToDisplayArea).isTrue();
- verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY),
- eq(false) /* shouldReportConfigChange */);
mController.detachIfNeeded();
diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java
index 83280f1..656e756 100644
--- a/core/tests/coretests/src/android/window/WindowContextTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
@@ -47,6 +48,8 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.frameworks.coretests.R;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -242,6 +245,12 @@
mInstrumentation.runOnMainSync(() -> wm.addView(subWindow, subWindowAttrs));
}
+ @Test
+ public void testGetCustomDrawable() {
+ assertNotNull(mWindowContext.getResources().getDrawable(R.drawable.custom_drawable,
+ null /* theme */));
+ }
+
private WindowContext createWindowContext() {
return createWindowContext(TYPE_APPLICATION_OVERLAY);
}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 756425e..0b8dc3f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -180,6 +180,7 @@
<assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" />
<assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" />
<assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="cameraserver" />
+ <assign-permission name="android.permission.REAL_GET_TASKS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 8ccf02ca..9584994 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -889,6 +889,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
},
+ "-1145384901": {
+ "message": "shouldWaitAnimatingExit: isTransition: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1142279614": {
"message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
"level": "VERBOSE",
@@ -1195,6 +1201,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-846931068": {
+ "message": "Update camera compat control state to %s for taskId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+ },
"-846078709": {
"message": "Configuration doesn't matter in finishing %s",
"level": "VERBOSE",
@@ -1273,6 +1285,12 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-743856570": {
+ "message": "shouldWaitAnimatingExit: isAnimating: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-743431900": {
"message": "Configuration no differences in %s",
"level": "VERBOSE",
@@ -1777,6 +1795,12 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
+ "-208825711": {
+ "message": "shouldWaitAnimatingExit: isWallpaperTarget: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-198463978": {
"message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
"level": "VERBOSE",
@@ -2989,6 +3013,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
},
+ "1087494661": {
+ "message": "Clear window stuck on animatingExit status: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"1088929964": {
"message": "onLockTaskPackagesUpdated: starting new locktask task=%s",
"level": "DEBUG",
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index fca4de9..2faa532 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -27,8 +27,9 @@
// Note: This field is accessed by native code.
public long mNativeObject; // BLASTBufferQueue*
- private static native long nativeCreate(String name, long surfaceControl, long width,
+ private static native long nativeCreateAndUpdate(String name, long surfaceControl, long width,
long height, int format);
+ private static native long nativeCreate(String name);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
@@ -38,6 +39,7 @@
long frameNumber);
private static native void nativeSetTransactionCompleteCallback(long ptr, long frameNumber,
TransactionCompleteCallback callback);
+ private static native long nativeGetLastAcquiredFrameNum(long ptr);
/**
* Callback sent to {@link #setTransactionCompleteCallback(long, TransactionCompleteCallback)}
@@ -53,7 +55,11 @@
/** Create a new connection with the surface flinger. */
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@PixelFormat.Format int format) {
- mNativeObject = nativeCreate(name, sc.mNativeObject, width, height, format);
+ mNativeObject = nativeCreateAndUpdate(name, sc.mNativeObject, width, height, format);
+ }
+
+ public BLASTBufferQueue(String name) {
+ mNativeObject = nativeCreate(name);
}
public void destroy() {
@@ -140,4 +146,7 @@
nativeMergeWithNextTransaction(mNativeObject, nativeTransaction, frameNumber);
}
+ public long getLastAcquiredFrameNum() {
+ return nativeGetLastAcquiredFrameNum(mNativeObject);
+ }
}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index c3b1cd74..14ad74e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -388,7 +388,8 @@
*/
public @NonNull FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
@NonNull Runnable frameCommitCallback) {
- setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
+ nSetFrameCommitCallback(mNativeProxy,
+ didProduceBuffer -> executor.execute(frameCommitCallback));
return this;
}
@@ -609,6 +610,11 @@
}
/** @hide */
+ public void setFrameCommitCallback(FrameCommitCallback callback) {
+ nSetFrameCommitCallback(mNativeProxy, callback);
+ }
+
+ /** @hide */
public void setFrameCompleteCallback(FrameCompleteCallback callback) {
nSetFrameCompleteCallback(mNativeProxy, callback);
}
@@ -904,13 +910,27 @@
*
* @hide
*/
+ public interface FrameCommitCallback {
+ /**
+ * Invoked after a new frame was drawn
+ *
+ * @param didProduceBuffer The draw successfully produced a new buffer.
+ */
+ void onFrameCommit(boolean didProduceBuffer);
+ }
+
+ /**
+ * Interface used to be notified when RenderThread has finished an attempt to draw. This doesn't
+ * mean a new frame has drawn, specifically if there's nothing new to draw, but only that
+ * RenderThread had a chance to draw a frame.
+ *
+ * @hide
+ */
public interface FrameCompleteCallback {
/**
- * Invoked after a frame draw
- *
- * @param frameNr The id of the frame that was drawn.
+ * Invoked after a frame draw was attempted.
*/
- void onFrameComplete(long frameNr);
+ void onFrameComplete();
}
/**
@@ -1362,6 +1382,9 @@
private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
+ private static native void nSetFrameCommitCallback(long nativeProxy,
+ FrameCommitCallback callback);
+
private static native void nSetFrameCompleteCallback(long nativeProxy,
FrameCompleteCallback callback);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
index 5619585..16f732f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -22,6 +22,8 @@
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.security.Key;
/**
@@ -46,7 +48,11 @@
// We do not include this member in comparisons.
private final KeyStoreSecurityLevel mSecurityLevel;
- AndroidKeyStoreKey(@NonNull KeyDescriptor descriptor,
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public AndroidKeyStoreKey(@NonNull KeyDescriptor descriptor,
long keyId,
@NonNull Authorization[] authorizations,
@NonNull String algorithm,
@@ -102,11 +108,9 @@
final int prime = 31;
int result = 1;
- result = prime * result + ((mDescriptor == null) ? 0 : mDescriptor.hashCode());
+ result = prime * result + getClass().hashCode();
result = prime * result + (int) (mKeyId >>> 32);
result = prime * result + (int) (mKeyId & 0xffffffff);
- result = prime * result + ((mAuthorizations == null) ? 0 : mAuthorizations.hashCode());
- result = prime * result + ((mAlgorithm == null) ? 0 : mAlgorithm.hashCode());
return result;
}
@@ -122,10 +126,6 @@
return false;
}
AndroidKeyStoreKey other = (AndroidKeyStoreKey) obj;
- if (mKeyId != other.mKeyId) {
- return false;
- }
-
- return true;
+ return mKeyId == other.mKeyId;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
index db3e567..0b3be32 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
@@ -23,6 +23,7 @@
import android.system.keystore2.KeyMetadata;
import java.security.PublicKey;
+import java.util.Arrays;
/**
* {@link PublicKey} backed by Android Keystore.
@@ -61,8 +62,8 @@
int result = 1;
result = prime * result + super.hashCode();
- result = prime * result + ((mCertificate == null) ? 0 : mCertificate.hashCode());
- result = prime * result + ((mCertificateChain == null) ? 0 : mCertificateChain.hashCode());
+ result = prime * result + Arrays.hashCode(mCertificate);
+ result = prime * result + Arrays.hashCode(mCertificateChain);
return result;
}
@@ -75,9 +76,14 @@
if (!super.equals(obj)) {
return false;
}
- if (getClass() != obj.getClass()) {
- return false;
- }
- return true;
+
+ /*
+ * getClass().equals(ojb.getClass()) is implied by the call to super.equals() above. This
+ * means we can cast obj to AndroidKeyStorePublicKey here.
+ */
+ final AndroidKeyStorePublicKey other = (AndroidKeyStorePublicKey) obj;
+
+ return Arrays.equals(mCertificate, other.mCertificate) && Arrays.equals(mCertificateChain,
+ other.mCertificateChain);
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 67358c4..33411e1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -601,8 +601,6 @@
}
KeyProtection params = (KeyProtection) param;
- @SecurityLevel int securityLevel = params.isStrongBoxBacked() ? SecurityLevel.STRONGBOX :
- SecurityLevel.TRUSTED_ENVIRONMENT;
@Domain int targetDomain = (getTargetDomain());
if (key instanceof AndroidKeyStoreSecretKey) {
@@ -794,6 +792,9 @@
flags |= IKeystoreSecurityLevel.KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
}
+ @SecurityLevel int securityLevel = params.isStrongBoxBacked() ? SecurityLevel.STRONGBOX :
+ SecurityLevel.TRUSTED_ENVIRONMENT;
+
try {
KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
securityLevel);
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 850c551..6fa1a69 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -89,7 +89,7 @@
// specific sensor (the one that hasn't changed), and 2) currently the only
// signal to developers is the UserNotAuthenticatedException, which doesn't
// indicate a specific sensor.
- boolean canUnlockViaBiometrics = true;
+ boolean canUnlockViaBiometrics = biometricSids.length > 0;
for (long sid : biometricSids) {
if (!keySids.contains(sid)) {
canUnlockViaBiometrics = false;
diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
index 1bd3069..f96c39c8 100644
--- a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
+++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
@@ -24,7 +24,13 @@
import android.security.KeyStore2;
import android.security.KeyStoreException;
+import android.security.KeyStoreSecurityLevel;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -52,4 +58,112 @@
verify(mKeystore2).list(anyInt(), anyLong());
}
+ @Mock
+ private KeyStoreSecurityLevel mKeystoreSecurityLevel;
+
+ private static KeyDescriptor descriptor() {
+ final KeyDescriptor keyDescriptor = new KeyDescriptor();
+ keyDescriptor.alias = "key";
+ keyDescriptor.blob = null;
+ keyDescriptor.domain = Domain.APP;
+ keyDescriptor.nspace = -1;
+ return keyDescriptor;
+ }
+
+ private static KeyMetadata metadata(byte[] cert, byte[] certChain) {
+ KeyMetadata metadata = new KeyMetadata();
+ metadata.authorizations = new Authorization[0];
+ metadata.certificate = cert;
+ metadata.certificateChain = certChain;
+ metadata.key = descriptor();
+ metadata.modificationTimeMs = 0;
+ metadata.keySecurityLevel = 1;
+ return metadata;
+ }
+
+ private static byte[] bytes(String string) {
+ return string.getBytes();
+ }
+
+ class MyPublicKey extends AndroidKeyStorePublicKey {
+ MyPublicKey(String cert, String chain, KeyStoreSecurityLevel securityLevel) {
+ super(descriptor(), metadata(cert.getBytes(), chain.getBytes()), "N/A".getBytes(),
+ "RSA", securityLevel);
+ }
+
+ @Override
+ AndroidKeyStorePrivateKey getPrivateKey() {
+ return null;
+ }
+ }
+
+ private AndroidKeyStorePublicKey makePrivateKeyObject(String cert, String chain) {
+ return new MyPublicKey(cert, chain, mKeystoreSecurityLevel);
+ }
+
+ @Test
+ public void testKeystoreKeysAdhereToContractBetweenEqualsAndHashCode() throws Exception {
+ AndroidKeyStoreKey key1 = new AndroidKeyStoreKey(descriptor(), 1, new Authorization[0],
+ "RSA", mKeystoreSecurityLevel);
+ AndroidKeyStoreKey key2 = new AndroidKeyStoreKey(descriptor(), 2, new Authorization[0],
+ "RSA", mKeystoreSecurityLevel);
+ AndroidKeyStoreKey key1_clone = new AndroidKeyStoreKey(descriptor(), 1,
+ new Authorization[0], "RSA", mKeystoreSecurityLevel);
+
+ assertThat("Identity should yield true", key1.equals(key1));
+ Assert.assertEquals("Identity should yield same hash codes",
+ key1.hashCode(), key1.hashCode());
+ assertThat("Identity should yield true", key2.equals(key2));
+ Assert.assertEquals("Identity should yield same hash codes",
+ key2.hashCode(), key2.hashCode());
+ assertThat("Different keys should differ", !key1.equals(key2));
+ Assert.assertNotEquals("Different keys should have different hash codes",
+ key1.hashCode(), key2.hashCode());
+
+ assertThat("Same keys should yield true", key1.equals(key1_clone));
+ assertThat("Same keys should yield true", key1_clone.equals(key1));
+ Assert.assertEquals("Same keys should yield same hash codes",
+ key1.hashCode(), key1_clone.hashCode());
+
+ assertThat("anything.equal(null) should yield false", !key1.equals(null));
+ assertThat("anything.equal(null) should yield false", !key2.equals(null));
+ assertThat("anything.equal(null) should yield false", !key1_clone.equals(null));
+ }
+
+ @Test
+ public void testKeystorePublicKeysAdhereToContractBetweenEqualsAndHashCode() throws Exception {
+ AndroidKeyStorePublicKey key1 = makePrivateKeyObject("myCert1", "myChain1");
+ AndroidKeyStorePublicKey key2 = makePrivateKeyObject("myCert2", "myChain1");
+ AndroidKeyStorePublicKey key3 = makePrivateKeyObject("myCert1", "myChain3");
+ AndroidKeyStorePublicKey key1_clone = makePrivateKeyObject("myCert1", "myChain1");
+
+ assertThat("Identity should yield true", key1.equals(key1));
+ Assert.assertEquals("Identity should yield same hash codes",
+ key1.hashCode(), key1.hashCode());
+ assertThat("Identity should yield true", key2.equals(key2));
+ Assert.assertEquals("Identity should yield same hash codes",
+ key2.hashCode(), key2.hashCode());
+ assertThat("Identity should yield true", key3.equals(key3));
+ Assert.assertEquals("Identity should yield same hash codes",
+ key3.hashCode(), key3.hashCode());
+ assertThat("Different keys should differ", !key1.equals(key2));
+ Assert.assertNotEquals("Different keys should have different hash codes",
+ key1.hashCode(), key2.hashCode());
+ assertThat("Different keys should differ", !key1.equals(key3));
+ Assert.assertNotEquals("Different keys should have different hash codes",
+ key1.hashCode(), key3.hashCode());
+ assertThat("Different keys should differ", !key2.equals(key3));
+ Assert.assertNotEquals("Different keys should have different hash codes",
+ key2.hashCode(), key3.hashCode());
+
+ assertThat("Same keys should yield true", key1.equals(key1_clone));
+ assertThat("Same keys should yield true", key1_clone.equals(key1));
+ Assert.assertEquals("Same keys should yield same hash codes",
+ key1.hashCode(), key1_clone.hashCode());
+
+ assertThat("anything.equal(null) should yield false", !key1.equals(null));
+ assertThat("anything.equal(null) should yield false", !key2.equals(null));
+ assertThat("anything.equal(null) should yield false", !key3.equals(null));
+ assertThat("anything.equal(null) should yield false", !key1_clone.equals(null));
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 85ef270..df751fc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -27,8 +27,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.view.SurfaceControl;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
@@ -51,9 +49,6 @@
/** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */
private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
- /** Mapping from the client assigned unique token to the TaskFragment {@link SurfaceControl}. */
- private final Map<IBinder, SurfaceControl> mFragmentLeashes = new ArrayMap<>();
-
/**
* Mapping from the client assigned unique token to the TaskFragment parent
* {@link Configuration}.
@@ -67,7 +62,7 @@
* Callback that notifies the controller about changes to task fragments.
*/
interface TaskFragmentCallback {
- void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
@@ -259,15 +254,12 @@
}
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
- final TaskFragmentInfo info = taskFragmentAppearedInfo.getTaskFragmentInfo();
- final IBinder fragmentToken = info.getFragmentToken();
- final SurfaceControl leash = taskFragmentAppearedInfo.getLeash();
- mFragmentInfos.put(fragmentToken, info);
- mFragmentLeashes.put(fragmentToken, leash);
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
+ mFragmentInfos.put(fragmentToken, taskFragmentInfo);
if (mCallback != null) {
- mCallback.onTaskFragmentAppeared(taskFragmentAppearedInfo);
+ mCallback.onTaskFragmentAppeared(taskFragmentInfo);
}
}
@@ -284,7 +276,6 @@
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
- mFragmentLeashes.remove(taskFragmentInfo.getFragmentToken());
mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
if (mCallback != null) {
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 68c1904..b8e8b01 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -37,7 +37,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -92,11 +91,13 @@
*/
public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
@Nullable Bundle options, @NonNull SplitRule sideRule,
- @NonNull Consumer<Exception> failureCallback) {
+ @Nullable Consumer<Exception> failureCallback) {
try {
mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule);
} catch (Exception e) {
- failureCallback.accept(e);
+ if (failureCallback != null) {
+ failureCallback.accept(e);
+ }
}
}
@@ -110,14 +111,13 @@
}
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
- TaskFragmentContainer container = getContainer(
- taskFragmentAppearedInfo.getTaskFragmentInfo().getFragmentToken());
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
if (container == null) {
return;
}
- container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
+ container.setInfo(taskFragmentInfo);
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
}
@@ -295,11 +295,12 @@
@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
@NonNull TaskFragmentContainer secondaryContainer,
@NonNull SplitRule splitRule) {
+ SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
+ secondaryContainer, splitRule);
+ // Remove container later to prevent pinning escaping toast showing in lock task mode.
if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
removeExistingSecondaryContainers(wct, primaryContainer);
}
- SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
- secondaryContainer, splitRule);
mSplitContainers.add(splitContainer);
}
@@ -860,4 +861,12 @@
launchingContainer.getTaskFragmentToken());
}
}
+
+ /**
+ * Checks if an activity is embedded and its presentation is customized by a
+ * {@link android.window.TaskFragmentOrganizer} to only occupy a portion of Task bounds.
+ */
+ public boolean isActivityEmbedded(@NonNull Activity activity) {
+ return mPresenter.isActivityEmbedded(activity.getActivityToken());
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 81be21c..ade5731 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -112,8 +112,7 @@
secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
// Set adjacent to each other so that the containers below will be invisible.
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
@@ -149,8 +148,7 @@
secondaryActivity, secondaryRectBounds, primaryContainer);
// Set adjacent to each other so that the containers below will be invisible.
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
@@ -269,8 +267,22 @@
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
+ }
+
+ private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer primaryContainer,
+ @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) {
+ final Rect parentBounds = getParentContainerBounds(primaryContainer);
+ // Clear adjacent TaskFragments if the container is shown in fullscreen, or the
+ // secondaryContainer could not be finished.
+ if (!shouldShowSideBySide(parentBounds, splitRule)) {
+ setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+ null /* secondary */, null /* splitRule */);
+ } else {
+ setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+ secondaryContainer.getTaskFragmentToken(), splitRule);
+ }
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index 8c8ef92..46bdf6d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -16,6 +16,7 @@
package androidx.window.extensions.embedding;
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
@@ -29,7 +30,7 @@
import android.animation.ValueAnimator;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerThread;
import android.os.RemoteException;
import android.util.Log;
import android.view.IRemoteAnimationFinishedCallback;
@@ -50,10 +51,14 @@
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
private static final String TAG = "TaskFragAnimationRunner";
- private final Handler mHandler = new Handler(Looper.myLooper());
+ private final Handler mHandler;
private final TaskFragmentAnimationSpec mAnimationSpec;
TaskFragmentAnimationRunner() {
+ HandlerThread animationThread = new HandlerThread(
+ "androidx.window.extensions.embedding", THREAD_PRIORITY_DISPLAY);
+ animationThread.start();
+ mHandler = animationThread.getThreadHandler();
mAnimationSpec = new TaskFragmentAnimationSpec(mHandler);
}
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 a1a53bc..4d2d055 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -103,7 +103,7 @@
ActivityThread activityThread = ActivityThread.currentActivityThread();
for (IBinder token : mInfo.getActivities()) {
Activity activity = activityThread.getActivity(token);
- if (activity != null && !allActivities.contains(activity)) {
+ if (activity != null && !activity.isFinishing() && !allActivities.contains(activity)) {
allActivities.add(activity);
}
}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index d6678bf..f54ab08 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 8e3d726..cdff585 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -43,6 +43,7 @@
name: "wm_shell_util-sources",
srcs: [
"src/com/android/wm/shell/util/**/*.java",
+ "src/com/android/wm/shell/common/split/SplitScreenConstants.java"
],
path: "src",
}
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
deleted file mode 100644
index e2c67fd..0000000
--- a/libs/WindowManager/Shell/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# sysui owners
-hwwang@google.com
-winsonc@google.com
-madym@google.com
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/color/compat_background_ripple.xml
similarity index 76%
rename from packages/SystemUI/res/color/prv_text_color_on_accent.xml
rename to libs/WindowManager/Shell/res/color/compat_background_ripple.xml
index 9f44aca..329e5b9 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/color/compat_background_ripple.xml
@@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/color/taskbar_background.xml
similarity index 76%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to libs/WindowManager/Shell/res/color/taskbar_background.xml
index 9f44aca..329e5b9 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/color/taskbar_background.xml
@@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml
new file mode 100644
index 0000000..1c8cb91
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml
@@ -0,0 +1,33 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <group>
+ <clip-path
+ android:pathData="M48,43l-48,-0l-0,-43l48,-0z"/>
+ <path
+ android:pathData="M24,43C37.2548,43 48,32.2548 48,19L48,0L0,-0L0,19C0,32.2548 10.7452,43 24,43Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M31,12.41L29.59,11L24,16.59L18.41,11L17,12.41L22.59,18L17,23.59L18.41,25L24,19.41L29.59,25L31,23.59L25.41,18L31,12.41Z"
+ android:fillColor="@color/compat_controls_text"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
similarity index 74%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
index 9f44aca..c810139 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_dismiss_button"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml
new file mode 100644
index 0000000..c796b59
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <path
+ android:pathData="M24,0C10.7452,0 0,10.7452 0,24V43H48V24C48,10.7452 37.2548,0 24,0Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M32,17H28.83L27,15H21L19.17,17H16C14.9,17 14,17.9 14,19V31C14,32.1 14.9,33 16,33H32C33.1,33 34,32.1 34,31V19C34,17.9 33.1,17 32,17ZM32,31H16V19H32V31Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M24.6618,22C23.0436,22 21.578,22.6187 20.4483,23.625L18.25,21.375V27H23.7458L21.5353,24.7375C22.3841,24.0125 23.4649,23.5625 24.6618,23.5625C26.8235,23.5625 28.6616,25.0062 29.3028,27L30.75,26.5125C29.9012,23.8938 27.5013,22 24.6618,22Z"
+ android:fillColor="@color/compat_controls_text"/>
+</vector>
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
similarity index 74%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
index 9f44aca..3e9fe6d 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_treatment_applied_button"/>
+</ripple>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml
new file mode 100644
index 0000000..af505d1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml
@@ -0,0 +1,53 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <path
+ android:pathData="M24,0C10.7452,0 0,10.7452 0,24V43H48V24C48,10.7452 37.2548,0 24,0Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M32,17H28.83L27,15H21L19.17,17H16C14.9,17 14,17.9 14,19V31C14,32.1 14.9,33 16,33H32C33.1,33 34,32.1 34,31V19C34,17.9 33.1,17 32,17ZM32,31H16V19H32V31Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,29L18,25.5L19.5,25.5L19.5,29L18,29Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,29L30,25.5L28.5,25.5L28.5,29L30,29Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,21L30,24.5L28.5,24.5L28.5,21L30,21Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,21L18,24.5L19.5,24.5L19.5,21L18,21Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,27.5L21.5,27.5L21.5,29L18,29L18,27.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,27.5L26.5,27.5L26.5,29L30,29L30,27.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,22.5L26.5,22.5L26.5,21L30,21L30,22.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,22.5L21.5,22.5L21.5,21L18,21L18,22.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+</vector>
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
similarity index 74%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
index 9f44aca..c0f1c89 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_treatment_suggested_button"/>
+</ripple>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
similarity index 85%
rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
rename to libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
index 94165a1..26848b1 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
@@ -16,6 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="@color/size_compat_hint_bubble"/>
- <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
+ <solid android:color="@color/compat_controls_background"/>
+ <corners android:radius="@dimen/compat_hint_corner_radius"/>
</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
similarity index 88%
rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
rename to libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
index a8f0f76..0e0ca37 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
+++ b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
@@ -15,11 +15,11 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/size_compat_hint_point_width"
+ android:width="@dimen/compat_hint_point_width"
android:height="8dp"
android:viewportWidth="10"
android:viewportHeight="8">
<path
- android:fillColor="@color/size_compat_hint_bubble"
+ android:fillColor="@color/compat_controls_background"
android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 3e486df..e6ae282 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -20,16 +20,18 @@
android:viewportWidth="48"
android:viewportHeight="48">
<path
- android:fillColor="#53534D"
- android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"
+ android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0"/>
<group
android:translateX="12"
android:translateY="12">
<path
- android:fillColor="#E4E3DA"
+ android:fillColor="@color/compat_controls_text"
android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
<path
- android:fillColor="#E4E3DA"
+ android:fillColor="@color/compat_controls_text"
android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
</group>
</vector>
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
similarity index 74%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
index 9f44aca..6551edf 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/size_compat_restart_button"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
new file mode 100644
index 0000000..4ac972c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:paddingEnd="@dimen/compat_hint_padding_end"
+ android:paddingBottom="5dp"
+ android:clickable="true">
+
+ <TextView
+ android:id="@+id/compat_mode_hint_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:lineSpacingExtra="4sp"
+ android:background="@drawable/compat_hint_bubble"
+ android:padding="16dp"
+ android:textAlignment="viewStart"
+ android:textColor="@color/compat_controls_text"
+ android:textSize="14sp"/>
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:src="@drawable/compat_hint_point"
+ android:paddingHorizontal="@dimen/compat_hint_corner_radius"
+ android:contentDescription="@null"/>
+
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
new file mode 100644
index 0000000..c99f3fe
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -0,0 +1,71 @@
+<?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.
+ -->
+<com.android.wm.shell.compatui.CompatUILayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="bottom|end">
+
+ <include android:id="@+id/camera_compat_hint"
+ android:visibility="gone"
+ android:layout_width="@dimen/camera_compat_hint_width"
+ android:layout_height="wrap_content"
+ layout="@layout/compat_mode_hint"/>
+
+ <LinearLayout
+ android:id="@+id/camera_compat_control"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:layout_marginEnd="16dp"
+ android:layout_marginBottom="16dp"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@+id/camera_compat_treatment_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"/>
+
+ <ImageButton
+ android:id="@+id/camera_compat_dismiss_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/camera_compat_dismiss_ripple"
+ android:background="@android:color/transparent"
+ android:contentDescription="@string/camera_compat_dismiss_button_description"/>
+
+ </LinearLayout>
+
+ <include android:id="@+id/size_compat_hint"
+ android:visibility="gone"
+ android:layout_width="@dimen/size_compat_hint_width"
+ android:layout_height="wrap_content"
+ layout="@layout/compat_mode_hint"/>
+
+ <ImageButton
+ android:id="@+id/size_compat_restart_button"
+ android:visibility="gone"
+ android:layout_width="@dimen/size_compat_button_width"
+ android:layout_height="@dimen/size_compat_button_height"
+ android:src="@drawable/size_compat_restart_button_ripple"
+ android:background="@android:color/transparent"
+ android:contentDescription="@string/restart_button_description"/>
+
+</com.android.wm.shell.compatui.CompatUILayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
deleted file mode 100644
index 17347f6..0000000
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.wm.shell.sizecompatui.SizeCompatHintPopup
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clipToPadding="false"
- android:paddingBottom="5dp">
-
- <LinearLayout
- android:id="@+id/size_compat_hint_popup"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:clickable="true">
-
- <TextView
- android:layout_width="188dp"
- android:layout_height="wrap_content"
- android:lineSpacingExtra="4sp"
- android:background="@drawable/size_compat_hint_bubble"
- android:padding="16dp"
- android:text="@string/restart_button_description"
- android:textAlignment="viewStart"
- android:textColor="#E4E3DA"
- android:textSize="14sp"/>
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:src="@drawable/size_compat_hint_point"
- android:paddingHorizontal="@dimen/size_compat_hint_corner_radius"
- android:contentDescription="@null"/>
-
- </LinearLayout>
-
- </FrameLayout>
-
-</com.android.wm.shell.sizecompatui.SizeCompatHintPopup>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
deleted file mode 100644
index 47e76f0..0000000
--- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<com.android.wm.shell.sizecompatui.SizeCompatRestartButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <FrameLayout
- android:layout_width="@dimen/size_compat_button_width"
- android:layout_height="@dimen/size_compat_button_height"
- android:clipToPadding="false"
- android:paddingBottom="16dp">
-
- <ImageButton
- android:id="@+id/size_compat_restart_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@drawable/size_compat_restart_button"
- android:background="@android:color/transparent"
- android:contentDescription="@string/restart_button_description"/>
-
- </FrameLayout>
-
-</com.android.wm.shell.sizecompatui.SizeCompatRestartButton>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 107da81..8b32b38 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index d724372..8b898c0 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
<string name="restart_button_description" msgid="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 7dd1f81..a9eafdd 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
<string name="restart_button_description" msgid="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 190f7ca..a81d4e3 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
<string name="restart_button_description" msgid="5887656107651190519">"এপ্টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index e33a35f..45e3fdb 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
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 f59e932..c17b3c4 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 3b478f2..bcf7186 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 3a77a1c..5f48744 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 8bfd775..f96d65f 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
<string name="restart_button_description" msgid="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index d23cc61..1e068c6 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 6434e31..97585ef 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 3530a7c..7a3a890 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 89b66e5..4a025ba 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index b49b446..bacd512 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index ed1d913..f306ba3 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 067e998..1ffbd039 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 067e998..1ffbd039 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 067e998..1ffbd039 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 067e998..1ffbd039 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 95c0d01..ffa3a65 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -73,4 +73,7 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <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>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 6e5347d..1ca7bfc 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 4820a0f..3d46421 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 4c94694..4f5d563 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index afc4292..b76ccc8 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index e8a0682..aef3c62 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
<string name="restart_button_description" msgid="5887656107651190519">"برای بازراهاندازی این برنامه و تغییر به حالت تمامصفحه، ضربه بزنید."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index f671105..5f76ba4 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 0d0b718..e1843e7 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 5652d7e..b3f3349 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 81bd916..3cdddf3 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 3d408cf2..09d206e 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
<string name="restart_button_description" msgid="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 8c93e0a..0ce5114 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
<string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 1f8f982..e8c83f1 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index ebd02e5..6d60513 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 29b2052..f1ec076 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
<string name="restart_button_description" msgid="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index e488855..62dfcb7 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Luaskan"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Setelan"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Masuk ke mode layar terpisah"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> adalah picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jika Anda tidak ingin <xliff:g id="NAME">%s</xliff:g> menggunakan fitur ini, ketuk untuk membuka setelan dan menonaktifkannya."</string>
@@ -74,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 126d1f1..feab362 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index ec221b1..bfcd385 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index b87d10c..7ef5035 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
<string name="restart_button_description" msgid="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 51ffca6..fb58abb 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
<string name="restart_button_description" msgid="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index fc91d72..297d9af 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
<string name="restart_button_description" msgid="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 05a905d..7ca2f23 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 6a1cb2b..dae2293 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោលសារលេចឡើង។"</string>
<string name="restart_button_description" msgid="5887656107651190519">"ចុចដើម្បីចាប់ផ្ដើមកម្មវិធីនេះឡើងវិញ រួចចូលប្រើពេញអេក្រង់។"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index aecb54b..72ba8ea 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="restart_button_description" msgid="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 5af9ca2a..d99abc4 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
<string name="restart_button_description" msgid="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 76f192e..fe25161 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 4ec6313..786d837 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
<string name="restart_button_description" msgid="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 8630e91..32bdfc4 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index b095b88..5d0f318 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 184fe9d..8c2e44c 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index f1bfe9a..6c8883a 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
<string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്റ്റാർട്ട് ചെയ്ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index afe8584..d297923 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Хаах"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Дэлгэх"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Тохиргоо"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Хуваасан дэлгэцийг оруулна уу"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Цэс"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> дэлгэцэн доторх дэлгэцэд байна"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Та <xliff:g id="NAME">%s</xliff:g>-д энэ онцлогийг ашиглуулахыг хүсэхгүй байвал тохиргоог нээгээд, үүнийг унтраана уу."</string>
@@ -74,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index c11af7b..3d9b53c 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
<string name="restart_button_description" msgid="5887656107651190519">"हे अॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index b495fef..0aafb59 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Kembangkan"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Tetapan"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Masuk skrin pisah"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> terdapat dalam gambar dalam gambar"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jika anda tidak mahu <xliff:g id="NAME">%s</xliff:g> menggunakan ciri ini, ketik untuk membuka tetapan dan matikan ciri."</string>
@@ -74,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 2849137..899c607 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ပိတ်ရန်"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ချဲ့ရန်"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ဆက်တင်များ"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသို့ ဝင်ရန်"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"မီနူး"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> သည် နှစ်ခုထပ်၍ကြည့်ခြင်း ဖွင့်ထားသည်"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> အား ဤဝန်ဆောင်မှုကို အသုံးမပြုစေလိုလျှင် ဆက်တင်ကိုဖွင့်ရန် တို့ပြီး ၎င်းဝန်ဆောင်မှုကို ပိတ်လိုက်ပါ။"</string>
@@ -74,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
<string name="restart_button_description" msgid="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 87bc7da..60891d3 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 12df158..8bd6907 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
<string name="restart_button_description" msgid="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index f83ad22..3fddf34 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 14f92c8..217556e 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
<string name="restart_button_description" msgid="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index e09f53adb..dc4dae1 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
<string name="restart_button_description" msgid="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index a2ef997..fccd138 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
<string name="restart_button_description" msgid="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 1300e53..5664030 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index f3314f8..abe6a01 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 1300e53..5664030 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 01f96c8..39dec90 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 6a0e9c1..e3c2215 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index d7ed246..efaf1f3 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
<string name="restart_button_description" msgid="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 13fd58f..a827153 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 6a68069..7437654 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 2b0b551..bda10c8 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Mbyll"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Zgjero"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Cilësimet"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Hyr në ekranin e ndarë"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyja"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> është në figurë brenda figurës"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Nëse nuk dëshiron që <xliff:g id="NAME">%s</xliff:g> ta përdorë këtë funksion, trokit për të hapur cilësimet dhe për ta çaktivizuar."</string>
@@ -74,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index c0c1e3f..98e13be 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 34254d9..3080088 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 82a9f14..9fb7460 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 0ed778a..d7f17aa 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
<string name="restart_button_description" msgid="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 8fef67b..4e421b5 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
<string name="restart_button_description" msgid="5887656107651190519">"ఈ యాప్ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్లోకి వెళ్లండి."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 563ec2f..52f94e6 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ปิด"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ขยาย"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"การตั้งค่า"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"เข้าสู่โหมดแบ่งหน้าจอ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"เมนู"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ใช้การแสดงภาพซ้อนภาพ"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"หากคุณไม่ต้องการให้ <xliff:g id="NAME">%s</xliff:g> ใช้ฟีเจอร์นี้ ให้แตะเพื่อเปิดการตั้งค่าแล้วปิดฟีเจอร์"</string>
@@ -74,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
<string name="restart_button_description" msgid="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index a9a36bb..d7b671d 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Isara"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Palawakin"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Mga Setting"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Pumasok sa split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Nasa picture-in-picture ang <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Kung ayaw mong magamit ni <xliff:g id="NAME">%s</xliff:g> ang feature na ito, i-tap upang buksan ang mga setting at i-off ito."</string>
@@ -74,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
<string name="restart_button_description" msgid="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 971520a..c618f6c 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 3014735..ec2e550 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 07319ef..18e1e51 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
<string name="restart_button_description" msgid="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 4c79d64..f6c7ed2 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index b9f23cd..1cab8e7 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index c007258..dc12c4f 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
<string name="restart_button_description" msgid="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 5e33677..fe114ac 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
<string name="restart_button_description" msgid="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 2439a97..0e4fc30 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
<string name="restart_button_description" msgid="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 20128f6..f563f1f 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -73,4 +73,10 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
+ <!-- no translation found for camera_compat_treatment_suggested_button_description (8103916969024076767) -->
+ <skip />
+ <!-- no translation found for camera_compat_treatment_applied_button_description (2944157113330703897) -->
+ <skip />
+ <!-- no translation found for camera_compat_dismiss_button_description (2795364433503817511) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index b25a218..cf596f7 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -29,7 +29,10 @@
<color name="bubbles_light">#FFFFFF</color>
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
- <color name="size_compat_hint_bubble">#30312B</color>
+
+ <!-- Compat controls UI -->
+ <color name="compat_controls_background">@android:color/system_neutral1_800</color>
+ <color name="compat_controls_text">@android:color/system_neutral1_50</color>
<!-- GM2 colors -->
<color name="GM2_grey_200">#E8EAED</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 9e77578..d338e3b 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -206,11 +206,21 @@
<!-- The height of the size compat restart button including padding. -->
<dimen name="size_compat_button_height">64dp</dimen>
- <!-- The radius of the corners of the size compat hint bubble. -->
- <dimen name="size_compat_hint_corner_radius">28dp</dimen>
+ <!-- The radius of the corners of the compat hint bubble. -->
+ <dimen name="compat_hint_corner_radius">28dp</dimen>
- <!-- The width of the size compat hint point. -->
- <dimen name="size_compat_hint_point_width">10dp</dimen>
+ <!-- The width of the compat hint point. -->
+ <dimen name="compat_hint_point_width">10dp</dimen>
+
+ <!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2
+ - compat_hint_corner_radius - compat_hint_point_width /2). -->
+ <dimen name="compat_hint_padding_end">7dp</dimen>
+
+ <!-- The width of the size compat hint. -->
+ <dimen name="size_compat_hint_width">188dp</dimen>
+
+ <!-- The width of the camera compat hint. -->
+ <dimen name="camera_compat_hint_width">143dp</dimen>
<!-- The width of the brand image on staring surface. -->
<dimen name="starting_surface_brand_image_width">200dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index c88fc16..ab0013a 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -158,4 +158,17 @@
<!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
<string name="restart_button_description">Tap to restart this app and go full screen.</string>
+
+ <!-- Description of the camera compat button for applying stretched issues treatment in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_treatment_suggested_button_description">Camera issues?\nTap to refit</string>
+
+ <!-- Description of the camera compat button for reverting stretched issues treatment in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_treatment_applied_button_description">Didn\u2019t fix it?\nTap to revert</string>
+
+ <!-- Accessibillity description of the camera dismiss button for stretched issues in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_dismiss_button_description">No camera issues? Tap to dismiss.</string>
+
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 358553d7..908a31d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -16,7 +16,7 @@
package com.android.wm.shell;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.ShellExecutor;
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 75bc461..91ea436 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -29,6 +29,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ActivityInfo;
@@ -51,8 +52,8 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.startingsurface.StartingWindowController;
import java.io.PrintWriter;
@@ -68,7 +69,7 @@
* TODO(b/167582004): may consider consolidating this class and TaskOrganizer
*/
public class ShellTaskOrganizer extends TaskOrganizer implements
- SizeCompatUIController.SizeCompatUICallback {
+ CompatUIController.CompatUICallback {
// Intentionally using negative numbers here so the positive numbers can be used
// for task id specific listeners that will be added later.
@@ -97,9 +98,9 @@
default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
default void onTaskVanished(RunningTaskInfo taskInfo) {}
default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
- /** Whether this task listener supports size compat UI. */
- default boolean supportSizeCompatUI() {
- // All TaskListeners should support size compat except PIP.
+ /** Whether this task listener supports compat UI. */
+ default boolean supportCompatUI() {
+ // All TaskListeners should support compat UI except PIP.
return true;
}
/** Attaches the a child window surface to the task surface. */
@@ -122,6 +123,16 @@
}
/**
+ * Callbacks for events in which the focus has changed.
+ */
+ public interface FocusListener {
+ /**
+ * Notifies when the task which is focused has changed.
+ */
+ void onFocusTaskChanged(RunningTaskInfo taskInfo);
+ }
+
+ /**
* Keys map from either a task id or {@link TaskListenerType}.
* @see #addListenerForTaskId
* @see #addListenerForType
@@ -142,46 +153,51 @@
/** @see #addLocusIdListener */
private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>();
+ private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>();
+
private final Object mLock = new Object();
private StartingWindowController mStartingWindow;
/**
- * In charge of showing size compat UI. Can be {@code null} if device doesn't support size
+ * In charge of showing compat UI. Can be {@code null} if device doesn't support size
* compat.
*/
@Nullable
- private final SizeCompatUIController mSizeCompatUI;
+ private final CompatUIController mCompatUI;
@Nullable
private final Optional<RecentTasksController> mRecentTasks;
+ @Nullable
+ private RunningTaskInfo mLastFocusedTaskInfo;
+
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
- this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
+ this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */,
Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
- SizeCompatUIController sizeCompatUI) {
- this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+ CompatUIController compatUI) {
+ this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
- SizeCompatUIController sizeCompatUI,
+ CompatUIController compatUI,
Optional<RecentTasksController> recentTasks) {
- this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+ this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
recentTasks);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
- Context context, @Nullable SizeCompatUIController sizeCompatUI,
+ Context context, @Nullable CompatUIController compatUI,
Optional<RecentTasksController> recentTasks) {
super(taskOrganizerController, mainExecutor);
- mSizeCompatUI = sizeCompatUI;
+ mCompatUI = compatUI;
mRecentTasks = recentTasks;
- if (sizeCompatUI != null) {
- sizeCompatUI.setSizeCompatUICallback(this);
+ if (compatUI != null) {
+ compatUI.setCompatUICallback(this);
}
}
@@ -200,6 +216,14 @@
}
}
+ @Override
+ public void unregisterOrganizer() {
+ super.unregisterOrganizer();
+ if (mStartingWindow != null) {
+ mStartingWindow.clearAllWindows();
+ }
+ }
+
public void createRootTask(int displayId, int windowingMode, TaskListener listener) {
ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s",
displayId, windowingMode, listener.toString());
@@ -331,6 +355,27 @@
}
}
+ /**
+ * Adds a listener to be notified for task focus changes.
+ */
+ public void addFocusListener(FocusListener listener) {
+ synchronized (mLock) {
+ mFocusListeners.add(listener);
+ if (mLastFocusedTaskInfo != null) {
+ listener.onFocusTaskChanged(mLastFocusedTaskInfo);
+ }
+ }
+ }
+
+ /**
+ * Removes listener.
+ */
+ public void removeLocusIdListener(FocusListener listener) {
+ synchronized (mLock) {
+ mFocusListeners.remove(listener);
+ }
+ }
+
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
if (mStartingWindow != null) {
@@ -383,7 +428,7 @@
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
notifyLocusVisibilityIfNeeded(info.getTaskInfo());
- notifySizeCompatUI(info.getTaskInfo(), listener);
+ notifyCompatUI(info.getTaskInfo(), listener);
}
/**
@@ -413,15 +458,27 @@
newListener.onTaskInfoChanged(taskInfo);
}
notifyLocusVisibilityIfNeeded(taskInfo);
- if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
- // Notify the size compat UI if the listener or task info changed.
- notifySizeCompatUI(taskInfo, newListener);
+ if (updated || !taskInfo.equalsForCompatUi(data.getTaskInfo())) {
+ // Notify the compat UI if the listener or task info changed.
+ notifyCompatUI(taskInfo, newListener);
}
if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) {
// Notify the recent tasks when a task changes windowing modes
mRecentTasks.ifPresent(recentTasks ->
recentTasks.onTaskWindowingModeChanged(taskInfo));
}
+ // TODO (b/207687679): Remove check for HOME once bug is fixed
+ final boolean isFocusedOrHome = taskInfo.isFocused
+ || (taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME
+ && taskInfo.isVisible);
+ final boolean focusTaskChanged = (mLastFocusedTaskInfo == null
+ || mLastFocusedTaskInfo.taskId != taskInfo.taskId) && isFocusedOrHome;
+ if (focusTaskChanged) {
+ for (int i = 0; i < mFocusListeners.size(); i++) {
+ mFocusListeners.valueAt(i).onFocusTaskChanged(taskInfo);
+ }
+ mLastFocusedTaskInfo = taskInfo;
+ }
}
}
@@ -447,8 +504,8 @@
listener.onTaskVanished(taskInfo);
}
notifyLocusVisibilityIfNeeded(taskInfo);
- // Pass null for listener to remove the size compat UI on this task if there is any.
- notifySizeCompatUI(taskInfo, null /* taskListener */);
+ // Pass null for listener to remove the compat UI on this task if there is any.
+ notifyCompatUI(taskInfo, null /* taskListener */);
// Notify the recent tasks that a task has been removed
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
}
@@ -550,6 +607,19 @@
restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
}
+ @Override
+ public void onCameraControlStateUpdated(
+ int taskId, @TaskInfo.CameraCompatControlState int state) {
+ final TaskAppearedInfo info;
+ synchronized (mLock) {
+ info = mTasks.get(taskId);
+ }
+ if (info == null) {
+ return;
+ }
+ updateCameraCompatControlState(info.getTaskInfo().token, state);
+ }
+
private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
int event) {
ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
@@ -561,29 +631,26 @@
}
/**
- * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task
+ * Notifies {@link CompatUIController} about the compat info changed on the give Task
* to update the UI accordingly.
*
* @param taskInfo the new Task info
* @param taskListener listener to handle the Task Surface placement. {@code null} if task is
* vanished.
*/
- private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
- if (mSizeCompatUI == null) {
+ private void notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
+ if (mCompatUI == null) {
return;
}
- // The task is vanished or doesn't support size compat UI, notify to remove size compat UI
+ // The task is vanished or doesn't support compat UI, notify to remove compat UI
// on this Task if there is any.
- if (taskListener == null || !taskListener.supportSizeCompatUI()
- || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) {
- mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- null /* taskConfig */, null /* taskListener */);
+ if (taskListener == null || !taskListener.supportCompatUI()
+ || !taskInfo.hasCompatUI() || !taskInfo.isVisible) {
+ mCompatUI.onCompatInfoChanged(taskInfo, null /* taskListener */);
return;
}
-
- mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- taskInfo.configuration, taskListener);
+ mCompatUI.onCompatInfoChanged(taskInfo, taskListener);
}
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index c8449a3..1f21937 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -21,9 +21,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+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 static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS
new file mode 100644
index 0000000..4d9b520
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-modules apppair owner
+chenghsiuchang@google.com
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 519a856..cd635c1 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
@@ -328,6 +328,7 @@
if (prevBubble == null) {
// Create a new bubble
bubble.setSuppressFlyout(suppressFlyout);
+ bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
doAdd(bubble);
trim();
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 300319a..b40021e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -32,6 +32,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -529,9 +530,10 @@
// Otherwise, we either tapped the stack (which means we're collapsed
// and should expand) or the currently selected bubble (we're expanded
// and should collapse).
- if (!maybeShowStackEdu()) {
+ if (!maybeShowStackEdu() && !mShowedUserEducationInTouchListenerActive) {
mBubbleData.setExpanded(!mBubbleData.isExpanded());
}
+ mShowedUserEducationInTouchListenerActive = false;
}
}
};
@@ -549,6 +551,14 @@
return true;
}
+ mShowedUserEducationInTouchListenerActive = false;
+ if (maybeShowStackEdu()) {
+ mShowedUserEducationInTouchListenerActive = true;
+ return true;
+ } else if (isStackEduShowing()) {
+ mStackEduView.hide(false /* fromExpansion */);
+ }
+
// If the manage menu is visible, just hide it.
if (mShowingManage) {
showManageMenu(false /* show */);
@@ -607,7 +617,8 @@
// If we're expanding or collapsing, ignore all touch events.
if (mIsExpansionAnimating
// Also ignore events if we shouldn't be draggable.
- || (mPositioner.showingInTaskbar() && !mIsExpanded)) {
+ || (mPositioner.showingInTaskbar() && !mIsExpanded)
+ || mShowedUserEducationInTouchListenerActive) {
return;
}
@@ -628,7 +639,7 @@
mExpandedAnimationController.dragBubbleOut(
v, viewInitialX + dx, viewInitialY + dy);
} else {
- if (mStackEduView != null) {
+ if (isStackEduShowing()) {
mStackEduView.hide(false /* fromExpansion */);
}
mStackAnimationController.moveStackFromTouch(
@@ -646,6 +657,10 @@
|| (mPositioner.showingInTaskbar() && !mIsExpanded)) {
return;
}
+ if (mShowedUserEducationInTouchListenerActive) {
+ mShowedUserEducationInTouchListenerActive = false;
+ return;
+ }
// First, see if the magnetized object consumes the event - if so, the bubble was
// released in the target or flung out of it, and we should ignore the event.
@@ -738,6 +753,7 @@
private ImageView mManageSettingsIcon;
private TextView mManageSettingsText;
private boolean mShowingManage = false;
+ private boolean mShowedUserEducationInTouchListenerActive = false;
private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
private BubblePositioner mPositioner;
@@ -929,10 +945,12 @@
showManageMenu(false /* show */);
} else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
mManageEduView.hide();
- } else if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ } else if (isStackEduShowing()) {
mStackEduView.hide(false /* isExpanding */);
} else if (mBubbleData.isExpanded()) {
mBubbleData.setExpanded(false);
+ } else {
+ maybeShowStackEdu();
}
});
@@ -1116,6 +1134,9 @@
* Whether the educational view should show for the expanded view "manage" menu.
*/
private boolean shouldShowManageEdu() {
+ if (ActivityManager.isRunningInTestHarness()) {
+ return false;
+ }
final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
&& mExpandedBubble != null;
@@ -1140,6 +1161,9 @@
* Whether education view should show for the collapsed stack.
*/
private boolean shouldShowStackEdu() {
+ if (ActivityManager.isRunningInTestHarness()) {
+ return false;
+ }
final boolean seen = getPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION);
final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
@@ -1157,7 +1181,7 @@
* @return true if education view for collapsed stack should show and was not showing before.
*/
private boolean maybeShowStackEdu() {
- if (!shouldShowStackEdu()) {
+ if (!shouldShowStackEdu() || isExpanded()) {
return false;
}
if (mStackEduView == null) {
@@ -1168,9 +1192,13 @@
return mStackEduView.show(mPositioner.getDefaultStartPosition());
}
+ private boolean isStackEduShowing() {
+ return mStackEduView != null && mStackEduView.getVisibility() == VISIBLE;
+ }
+
// Recreates & shows the education views. Call when a theme/config change happens.
private void updateUserEdu() {
- if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ if (isStackEduShowing()) {
removeView(mStackEduView);
mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
addView(mStackEduView);
@@ -1852,7 +1880,7 @@
cancelDelayedExpandCollapseSwitchAnimations();
final boolean showVertically = mPositioner.showBubblesVertically();
mIsExpanded = true;
- if (mStackEduView != null) {
+ if (isStackEduShowing()) {
mStackEduView.hide(true /* fromExpansion */);
}
beforeExpandedViewAnimation();
@@ -2390,7 +2418,7 @@
if (flyoutMessage == null
|| flyoutMessage.message == null
|| !bubble.showFlyout()
- || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE)
+ || isStackEduShowing()
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
@@ -2512,7 +2540,7 @@
* them.
*/
public void getTouchableRegion(Rect outRect) {
- if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ if (isStackEduShowing()) {
// When user education shows then capture all touches
outRect.set(0, 0, getWidth(), getHeight());
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS
new file mode 100644
index 0000000..8271014
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module bubble owner
+madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index f6a90b7..3846de7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -125,6 +125,7 @@
* @return true if user education was shown, false otherwise.
*/
fun show(stackPosition: PointF): Boolean {
+ isHiding = false
if (visibility == VISIBLE) return false
controller.updateWindowFlagsForBackpress(true /* interceptBack */)
@@ -164,6 +165,7 @@
*/
fun hide(isExpanding: Boolean) {
if (visibility != VISIBLE || isHiding) return
+ isHiding = true
controller.updateWindowFlagsForBackpress(false /* interceptBack */)
animate()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index f0f78748..19d513f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -346,6 +346,9 @@
* bubble is dragged back into the row.
*/
public void dragBubbleOut(View bubbleView, float x, float y) {
+ if (mMagnetizedBubbleDraggingOut == null) {
+ return;
+ }
if (mSpringToTouchOnNextMotionEvent) {
springBubbleTo(mMagnetizedBubbleDraggingOut.getUnderlyingObject(), x, y);
mSpringToTouchOnNextMotionEvent = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 625bcee..ba343cb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -29,11 +29,14 @@
import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
+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 static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -60,36 +63,15 @@
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+
+import java.io.PrintWriter;
/**
* Records and handles layout of splits. Helps to calculate proper bounds when configuration or
* divide position changes.
*/
public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
- /**
- * Split position isn't specified normally meaning to use what ever it is currently set to.
- */
- public static final int SPLIT_POSITION_UNDEFINED = -1;
-
- /**
- * Specifies that a split is positioned at the top half of the screen if
- * in portrait mode or at the left half of the screen if in landscape mode.
- */
- public static final int SPLIT_POSITION_TOP_OR_LEFT = 0;
-
- /**
- * Specifies that a split is positioned at the bottom half of the screen if
- * in portrait mode or at the right half of the screen if in landscape mode.
- */
- public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;
-
- @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
- SPLIT_POSITION_UNDEFINED,
- SPLIT_POSITION_TOP_OR_LEFT,
- SPLIT_POSITION_BOTTOM_OR_RIGHT
- })
- public @interface SplitPosition {
- }
private final int mDividerWindowWidth;
private final int mDividerInsets;
@@ -318,6 +300,16 @@
mSplitLayoutHandler.onLayoutSizeChanged(this);
}
+ /** Sets divide position base on the ratio within root bounds. */
+ public void setDivideRatio(float ratio) {
+ final int position = isLandscape()
+ ? mRootBounds.left + (int) (mRootBounds.width() * ratio)
+ : mRootBounds.top + (int) (mRootBounds.height() * ratio);
+ DividerSnapAlgorithm.SnapTarget snapTarget =
+ mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+ setDividePosition(snapTarget.position);
+ }
+
/** Resets divider position. */
public void resetDividerPosition() {
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
@@ -415,6 +407,19 @@
return bounds.width() > bounds.height();
}
+ /** Reverse the split position. */
+ @SplitPosition
+ public static int reversePosition(@SplitPosition int position) {
+ switch (position) {
+ case SPLIT_POSITION_TOP_OR_LEFT:
+ return SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ case SPLIT_POSITION_BOTTOM_OR_RIGHT:
+ return SPLIT_POSITION_TOP_OR_LEFT;
+ default:
+ return SPLIT_POSITION_UNDEFINED;
+ }
+ }
+
/**
* Return if this layout is landscape.
*/
@@ -502,6 +507,13 @@
}
}
+ /** Dumps the current split bounds recorded in this layout. */
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ pw.println(prefix + "bounds1=" + mBounds1.toShortString());
+ pw.println(prefix + "dividerBounds=" + mDividerBounds.toShortString());
+ pw.println(prefix + "bounds2=" + mBounds2.toShortString());
+ }
+
/** Handles layout change event. */
public interface SplitLayoutHandler {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
new file mode 100644
index 0000000..9b61487
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
@@ -0,0 +1,47 @@
+/*
+ * 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.wm.shell.common.split;
+
+import android.annotation.IntDef;
+
+/** Helper utility class of methods and constants that are available to be imported in Launcher. */
+public class SplitScreenConstants {
+
+ /**
+ * Split position isn't specified normally meaning to use what ever it is currently set to.
+ */
+ public static final int SPLIT_POSITION_UNDEFINED = -1;
+
+ /**
+ * Specifies that a split is positioned at the top half of the screen if
+ * in portrait mode or at the left half of the screen if in landscape mode.
+ */
+ public static final int SPLIT_POSITION_TOP_OR_LEFT = 0;
+
+ /**
+ * Specifies that a split is positioned at the bottom half of the screen if
+ * in portrait mode or at the right half of the screen if in landscape mode.
+ */
+ public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;
+
+ @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
+ SPLIT_POSITION_UNDEFINED,
+ SPLIT_POSITION_TOP_OR_LEFT,
+ SPLIT_POSITION_BOTTOM_OR_RIGHT
+ })
+ public @interface SplitPosition {
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
similarity index 86%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
index a703114..99dbfe0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
import com.android.wm.shell.common.annotations.ExternalThread;
/**
- * Interface to engage size compat UI.
+ * Interface to engage compat UI.
*/
@ExternalThread
-public interface SizeCompatUI {
+public interface CompatUI {
/**
- * Called when the keyguard occluded state changes. Removes all size compat UIs if the
+ * Called when the keyguard occluded state changes. Removes all compat UIs if the
* keyguard is now occluded.
* @param occluded indicates if the keyguard is now occluded.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
similarity index 70%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index e06070a..8f4cfb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
@@ -48,20 +50,22 @@
/**
* Controls to show/update restart-activity buttons on Tasks based on whether the foreground
- * activities are in size compatibility mode.
+ * activities are in compatibility mode.
*/
-public class SizeCompatUIController implements OnDisplaysChangedListener,
+public class CompatUIController implements OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor {
- /** Callback for size compat UI interaction. */
- public interface SizeCompatUICallback {
+ /** Callback for compat UI interaction. */
+ public interface CompatUICallback {
/** Called when the size compat restart button appears. */
void onSizeCompatRestartButtonAppeared(int taskId);
/** Called when the size compat restart button is clicked. */
void onSizeCompatRestartButtonClicked(int taskId);
+ /** Called when the camera compat control state is updated. */
+ void onCameraControlStateUpdated(int taskId, @CameraCompatControlState int state);
}
- private static final String TAG = "SizeCompatUIController";
+ private static final String TAG = "CompatUIController";
/** Whether the IME is shown on display id. */
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
@@ -71,7 +75,7 @@
new SparseArray<>(0);
/** The showing UIs by task id. */
- private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
+ private final SparseArray<CompatUIWindowManager> mActiveLayouts = new SparseArray<>(0);
/** Avoid creating display context frequently for non-default display. */
private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
@@ -82,17 +86,19 @@
private final DisplayImeController mImeController;
private final SyncTransactionQueue mSyncQueue;
private final ShellExecutor mMainExecutor;
- private final SizeCompatUIImpl mImpl = new SizeCompatUIImpl();
+ private final CompatUIImpl mImpl = new CompatUIImpl();
- private SizeCompatUICallback mCallback;
+ private CompatUICallback mCallback;
- /** Only show once automatically in the process life. */
- private boolean mHasShownHint;
- /** Indicates if the keyguard is currently occluded, in which case size compat UIs shouldn't
- * be shown. */
+ // Only show once automatically in the process life.
+ private boolean mHasShownSizeCompatHint;
+ private boolean mHasShownCameraCompatHint;
+
+ // Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
+ // be shown.
private boolean mKeyguardOccluded;
- public SizeCompatUIController(Context context,
+ public CompatUIController(Context context,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
DisplayImeController imeController,
@@ -108,36 +114,34 @@
mImeController.addPositionProcessor(this);
}
- public SizeCompatUI asSizeCompatUI() {
+ /** Returns implementation of {@link CompatUI}. */
+ public CompatUI asCompatUI() {
return mImpl;
}
/** Sets the callback for UI interactions. */
- public void setSizeCompatUICallback(SizeCompatUICallback callback) {
+ public void setCompatUICallback(CompatUICallback callback) {
mCallback = callback;
}
/**
- * Called when the Task info changed. Creates and updates the size compat UI if there is an
+ * Called when the Task info changed. Creates and updates the compat UI if there is an
* activity in size compat, or removes the UI if there is no size compat activity.
*
- * @param displayId display the task and activity are in.
- * @param taskId task the activity is in.
- * @param taskConfig task config to place the size compat UI with.
+ * @param taskInfo {@link TaskInfo} task the activity is in.
* @param taskListener listener to handle the Task Surface placement.
*/
- public void onSizeCompatInfoChanged(int displayId, int taskId,
- @Nullable Configuration taskConfig,
+ public void onCompatInfoChanged(TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
- if (taskConfig == null || taskListener == null) {
- // Null token means the current foreground activity is not in size compatibility mode.
- removeLayout(taskId);
- } else if (mActiveLayouts.contains(taskId)) {
+ if (taskInfo.configuration == null || taskListener == null) {
+ // Null token means the current foreground activity is not in compatibility mode.
+ removeLayout(taskInfo.taskId);
+ } else if (mActiveLayouts.contains(taskInfo.taskId)) {
// UI already exists, update the UI layout.
- updateLayout(taskId, taskConfig, taskListener);
+ updateLayout(taskInfo, taskListener);
} else {
- // Create a new size compat UI.
- createLayout(displayId, taskId, taskConfig, taskListener);
+ // Create a new compat UI.
+ createLayout(taskInfo, taskListener);
}
}
@@ -151,7 +155,7 @@
mDisplayContextCache.remove(displayId);
removeOnInsetsChangedListener(displayId);
- // Remove all size compat UIs on the removed display.
+ // Remove all compat UIs on the removed display.
final List<Integer> toRemoveTaskIds = new ArrayList<>();
forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId()));
for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) {
@@ -194,7 +198,7 @@
mDisplaysWithIme.remove(displayId);
}
- // Hide the size compat UIs when input method is showing.
+ // Hide the compat UIs when input method is showing.
forAllLayoutsOnDisplay(displayId,
layout -> layout.updateVisibility(showOnDisplay(displayId)));
}
@@ -202,7 +206,7 @@
@VisibleForTesting
void onKeyguardOccludedChanged(boolean occluded) {
mKeyguardOccluded = occluded;
- // Hide the size compat UIs when keyguard is occluded.
+ // Hide the compat UIs when keyguard is occluded.
forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
}
@@ -214,42 +218,49 @@
return mDisplaysWithIme.contains(displayId);
}
- private void createLayout(int displayId, int taskId, Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener) {
- final Context context = getOrCreateDisplayContext(displayId);
+ private void createLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
- Log.e(TAG, "Cannot get context for display " + displayId);
+ Log.e(TAG, "Cannot get context for display " + taskInfo.displayId);
return;
}
- final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig,
- taskListener);
- mActiveLayouts.put(taskId, layout);
- layout.createSizeCompatButton(showOnDisplay(displayId));
+ final CompatUIWindowManager compatUIWindowManager =
+ createLayout(context, taskInfo, taskListener);
+ mActiveLayouts.put(taskInfo.taskId, compatUIWindowManager);
+ compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId),
+ taskInfo.topActivityInSizeCompat, taskInfo.cameraCompatControlState);
}
@VisibleForTesting
- SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
- Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
- final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, mCallback, context,
- taskConfig, taskId, taskListener, mDisplayController.getDisplayLayout(displayId),
- mHasShownHint);
- // Only show hint for the first time.
- mHasShownHint = true;
- return layout;
+ CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
+ final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
+ taskInfo.configuration, mSyncQueue, mCallback, taskInfo.taskId, taskListener,
+ mDisplayController.getDisplayLayout(taskInfo.displayId), mHasShownSizeCompatHint,
+ mHasShownCameraCompatHint);
+ // Only show hints for the first time.
+ if (taskInfo.topActivityInSizeCompat) {
+ mHasShownSizeCompatHint = true;
+ }
+ if (taskInfo.hasCameraCompatControl()) {
+ mHasShownCameraCompatHint = true;
+ }
+ return compatUIWindowManager;
}
- private void updateLayout(int taskId, Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener) {
- final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ private void updateLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskInfo.taskId);
if (layout == null) {
return;
}
- layout.updateSizeCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
+ layout.updateCompatInfo(taskInfo.configuration, taskListener,
+ showOnDisplay(layout.getDisplayId()), taskInfo.topActivityInSizeCompat,
+ taskInfo.cameraCompatControlState);
}
private void removeLayout(int taskId) {
- final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
if (layout != null) {
layout.release();
mActiveLayouts.remove(taskId);
@@ -275,19 +286,19 @@
return context;
}
- private void forAllLayoutsOnDisplay(int displayId, Consumer<SizeCompatUILayout> callback) {
+ private void forAllLayoutsOnDisplay(int displayId, Consumer<CompatUIWindowManager> callback) {
forAllLayouts(layout -> layout.getDisplayId() == displayId, callback);
}
- private void forAllLayouts(Consumer<SizeCompatUILayout> callback) {
+ private void forAllLayouts(Consumer<CompatUIWindowManager> callback) {
forAllLayouts(layout -> true, callback);
}
- private void forAllLayouts(Predicate<SizeCompatUILayout> condition,
- Consumer<SizeCompatUILayout> callback) {
+ private void forAllLayouts(Predicate<CompatUIWindowManager> condition,
+ Consumer<CompatUIWindowManager> callback) {
for (int i = 0; i < mActiveLayouts.size(); i++) {
final int taskId = mActiveLayouts.keyAt(i);
- final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
if (layout != null && condition.test(layout)) {
callback.accept(layout);
}
@@ -298,11 +309,11 @@
* The interface for calls from outside the Shell, within the host process.
*/
@ExternalThread
- private class SizeCompatUIImpl implements SizeCompatUI {
+ private class CompatUIImpl implements CompatUI {
@Override
public void onKeyguardOccludedChanged(boolean occluded) {
mMainExecutor.execute(() -> {
- SizeCompatUIController.this.onKeyguardOccludedChanged(occluded);
+ CompatUIController.this.onKeyguardOccludedChanged(occluded);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
new file mode 100644
index 0000000..29b2baa
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -0,0 +1,151 @@
+/*
+ * 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.wm.shell.compatui;
+
+import android.annotation.IdRes;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Container for compat UI controls.
+ */
+public class CompatUILayout extends LinearLayout {
+
+ private CompatUIWindowManager mWindowManager;
+
+ public CompatUILayout(Context context) {
+ this(context, null);
+ }
+
+ public CompatUILayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ void inject(CompatUIWindowManager windowManager) {
+ mWindowManager = windowManager;
+ }
+
+ void updateCameraTreatmentButton(@CameraCompatControlState int newState) {
+ int buttonBkgId = newState == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? R.drawable.camera_compat_treatment_suggested_ripple
+ : R.drawable.camera_compat_treatment_applied_ripple;
+ int hintStringId = newState == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? R.string.camera_compat_treatment_suggested_button_description
+ : R.string.camera_compat_treatment_applied_button_description;
+ final ImageButton button = findViewById(R.id.camera_compat_treatment_button);
+ button.setImageResource(buttonBkgId);
+ button.setContentDescription(getResources().getString(hintStringId));
+ final LinearLayout hint = findViewById(R.id.camera_compat_hint);
+ ((TextView) hint.findViewById(R.id.compat_mode_hint_text)).setText(hintStringId);
+ }
+
+ void setSizeCompatHintVisibility(boolean show) {
+ setViewVisibility(R.id.size_compat_hint, show);
+ }
+
+ void setCameraCompatHintVisibility(boolean show) {
+ setViewVisibility(R.id.camera_compat_hint, show);
+ }
+
+ void setRestartButtonVisibility(boolean show) {
+ setViewVisibility(R.id.size_compat_restart_button, show);
+ // Hint should never be visible without button.
+ if (!show) {
+ setSizeCompatHintVisibility(/* show= */ false);
+ }
+ }
+
+ void setCameraControlVisibility(boolean show) {
+ setViewVisibility(R.id.camera_compat_control, show);
+ // Hint should never be visible without button.
+ if (!show) {
+ setCameraCompatHintVisibility(/* show= */ false);
+ }
+ }
+
+ private void setViewVisibility(@IdRes int resId, boolean show) {
+ final View view = findViewById(resId);
+ int visibility = show ? View.VISIBLE : View.GONE;
+ if (view.getVisibility() == visibility) {
+ return;
+ }
+ view.setVisibility(visibility);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ // Need to relayout after changes like hiding / showing a hint since they affect size.
+ // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
+ mWindowManager.relayout();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
+ restartButton.setOnClickListener(view -> mWindowManager.onRestartButtonClicked());
+ restartButton.setOnLongClickListener(view -> {
+ mWindowManager.onRestartButtonLongClicked();
+ return true;
+ });
+
+ final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+ ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
+ .setText(R.string.restart_button_description);
+ sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
+
+ final ImageButton cameraTreatmentButton =
+ findViewById(R.id.camera_compat_treatment_button);
+ cameraTreatmentButton.setOnClickListener(
+ view -> mWindowManager.onCameraTreatmentButtonClicked());
+ cameraTreatmentButton.setOnLongClickListener(view -> {
+ mWindowManager.onCameraButtonLongClicked();
+ return true;
+ });
+
+ final ImageButton cameraDismissButton = findViewById(R.id.camera_compat_dismiss_button);
+ cameraDismissButton.setOnClickListener(
+ view -> mWindowManager.onCameraDismissButtonClicked());
+ cameraDismissButton.setOnLongClickListener(view -> {
+ mWindowManager.onCameraButtonLongClicked();
+ return true;
+ });
+
+ final LinearLayout cameraCompatHint = findViewById(R.id.camera_compat_hint);
+ cameraCompatHint.setOnClickListener(
+ view -> setCameraCompatHintVisibility(/* show= */ false));
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
new file mode 100644
index 0000000..44526b0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -0,0 +1,417 @@
+/*
+ * 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.wm.shell.compatui;
+
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+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_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.app.TaskInfo.CameraCompatControlState;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat
+ * controls.
+ */
+class CompatUIWindowManager extends WindowlessWindowManager {
+
+ private static final String TAG = "CompatUIWindowManager";
+
+ private final SyncTransactionQueue mSyncQueue;
+ private final CompatUIController.CompatUICallback mCallback;
+ private final int mDisplayId;
+ private final int mTaskId;
+ private final Rect mStableBounds;
+
+ private Context mContext;
+ private Configuration mTaskConfig;
+ private ShellTaskOrganizer.TaskListener mTaskListener;
+ private DisplayLayout mDisplayLayout;
+
+ // Remember the last reported states in case visibility changes due to keyguard or
+ // IME updates.
+ @VisibleForTesting
+ boolean mHasSizeCompat;
+ @CameraCompatControlState
+ private int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
+
+ @VisibleForTesting
+ boolean mShouldShowSizeCompatHint;
+ @VisibleForTesting
+ boolean mShouldShowCameraCompatHint;
+
+ @Nullable
+ @VisibleForTesting
+ CompatUILayout mCompatUILayout;
+
+ @Nullable
+ private SurfaceControlViewHost mViewHost;
+ @Nullable
+ private SurfaceControl mLeash;
+
+ CompatUIWindowManager(Context context, Configuration taskConfig,
+ SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
+ int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
+ boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
+ super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
+ mContext = context;
+ mSyncQueue = syncQueue;
+ mCallback = callback;
+ mTaskConfig = taskConfig;
+ mDisplayId = mContext.getDisplayId();
+ mTaskId = taskId;
+ mTaskListener = taskListener;
+ mDisplayLayout = displayLayout;
+ mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
+ mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
+ mStableBounds = new Rect();
+ mDisplayLayout.getStableBounds(mStableBounds);
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ mContext = mContext.createConfigurationContext(configuration);
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName("CompatUILeash")
+ .setHidden(false)
+ .setCallsite("CompatUIWindowManager#attachToParentSurface");
+ attachToParentSurface(builder);
+ mLeash = builder.build();
+ b.setParent(mLeash);
+ }
+
+ /** Creates the layout for compat controls. */
+ void createLayout(boolean show, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ mHasSizeCompat = hasSizeCompat;
+ mCameraCompatControlState = cameraCompatControlState;
+ if (!show || mCompatUILayout != null) {
+ // Wait until compat controls should be visible.
+ return;
+ }
+
+ initCompatUi();
+ updateSurfacePosition();
+
+ if (hasSizeCompat) {
+ mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+ }
+ }
+
+ private void createLayout(boolean show) {
+ createLayout(show, mHasSizeCompat, mCameraCompatControlState);
+ }
+
+ /** Called when compat info changed. */
+ void updateCompatInfo(Configuration taskConfig,
+ ShellTaskOrganizer.TaskListener taskListener, boolean show, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ final Configuration prevTaskConfig = mTaskConfig;
+ final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
+ mTaskConfig = taskConfig;
+ mTaskListener = taskListener;
+ final boolean prevHasSizeCompat = mHasSizeCompat;
+ final int prevCameraCompatControlState = mCameraCompatControlState;
+ mHasSizeCompat = hasSizeCompat;
+ mCameraCompatControlState = cameraCompatControlState;
+
+ // Update configuration.
+ mContext = mContext.createConfigurationContext(taskConfig);
+ setConfiguration(taskConfig);
+
+ if (mCompatUILayout == null || prevTaskListener != taskListener) {
+ // TaskListener changed, recreate the layout for new surface parent.
+ release();
+ createLayout(show);
+ return;
+ }
+
+ if (prevHasSizeCompat != mHasSizeCompat
+ || prevCameraCompatControlState != mCameraCompatControlState) {
+ updateVisibilityOfViews();
+ }
+
+ if (!taskConfig.windowConfiguration.getBounds()
+ .equals(prevTaskConfig.windowConfiguration.getBounds())) {
+ // Reposition the UI surfaces.
+ updateSurfacePosition();
+ }
+
+ if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
+ // Update layout for RTL.
+ mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
+ updateSurfacePosition();
+ }
+
+ }
+
+ /** Called when the visibility of the UI should change. */
+ void updateVisibility(boolean show) {
+ if (mCompatUILayout == null) {
+ // Layout may not have been created because it was hidden previously.
+ createLayout(show);
+ return;
+ }
+
+ // Hide compat UIs when IME is showing.
+ final int newVisibility = show ? View.VISIBLE : View.GONE;
+ if (mCompatUILayout.getVisibility() != newVisibility) {
+ mCompatUILayout.setVisibility(newVisibility);
+ }
+ }
+
+ /** Called when display layout changed. */
+ void updateDisplayLayout(DisplayLayout displayLayout) {
+ final Rect prevStableBounds = mStableBounds;
+ final Rect curStableBounds = new Rect();
+ displayLayout.getStableBounds(curStableBounds);
+ mDisplayLayout = displayLayout;
+ if (!prevStableBounds.equals(curStableBounds)) {
+ // Stable bounds changed, update UI surface positions.
+ updateSurfacePosition();
+ mStableBounds.set(curStableBounds);
+ }
+ }
+
+ /** Called when it is ready to be placed compat UI surface. */
+ void attachToParentSurface(SurfaceControl.Builder b) {
+ mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+ }
+
+ /** Called when the restart button is clicked. */
+ void onRestartButtonClicked() {
+ mCallback.onSizeCompatRestartButtonClicked(mTaskId);
+ }
+
+ /** Called when the camera treatment button is clicked. */
+ void onCameraTreatmentButtonClicked() {
+ if (!shouldShowCameraControl()) {
+ Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ return;
+ }
+ // When a camera control is shown, only two states are allowed: "treament applied" and
+ // "treatment suggested". Clicks on the conrol's treatment button toggle between these
+ // two states.
+ mCameraCompatControlState =
+ mCameraCompatControlState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
+ : CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState);
+ mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ }
+
+ /** Called when the camera dismiss button is clicked. */
+ void onCameraDismissButtonClicked() {
+ if (!shouldShowCameraControl()) {
+ Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ return;
+ }
+ mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
+ mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED);
+ mCompatUILayout.setCameraControlVisibility(/* show= */ false);
+ }
+
+ /** Called when the restart button is long clicked. */
+ void onRestartButtonLongClicked() {
+ if (mCompatUILayout == null) {
+ return;
+ }
+ mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ }
+
+ /** Called when either dismiss or treatment camera buttons is long clicked. */
+ void onCameraButtonLongClicked() {
+ if (mCompatUILayout == null) {
+ return;
+ }
+ mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ /** Releases the surface control and tears down the view hierarchy. */
+ void release() {
+ // Hiding before releasing to avoid flickering when transitioning to the Home screen.
+ mCompatUILayout.setVisibility(View.GONE);
+ mCompatUILayout = null;
+
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mLeash != null) {
+ final SurfaceControl leash = mLeash;
+ mSyncQueue.runInSync(t -> t.remove(leash));
+ mLeash = null;
+ }
+ }
+
+ void relayout() {
+ mViewHost.relayout(getWindowLayoutParams());
+ updateSurfacePosition();
+ }
+
+ @VisibleForTesting
+ void updateSurfacePosition() {
+ if (mCompatUILayout == null || mLeash == null) {
+ return;
+ }
+
+ // Use stable bounds to prevent controls from overlapping with system bars.
+ final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ mDisplayLayout.getStableBounds(stableBounds);
+ stableBounds.intersect(taskBounds);
+
+ // Position of the button in the container coordinate.
+ final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? stableBounds.left - taskBounds.left
+ : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth();
+ final int positionY = stableBounds.bottom - taskBounds.top
+ - mCompatUILayout.getMeasuredHeight();
+
+ updateSurfacePosition(positionX, positionY);
+ }
+
+ private int getLayoutDirection() {
+ return mContext.getResources().getConfiguration().getLayoutDirection();
+ }
+
+ private void updateSurfacePosition(int positionX, int positionY) {
+ mSyncQueue.runInSync(t -> {
+ if (mLeash == null || !mLeash.isValid()) {
+ Log.w(TAG, "The leash has been released.");
+ return;
+ }
+ t.setPosition(mLeash, positionX, positionY);
+ // The compat UI should be the topmost child of the Task in case there can be more
+ // than one children.
+ t.setLayer(mLeash, Integer.MAX_VALUE);
+ });
+ }
+
+ /** Inflates {@link CompatUILayout} on to the root surface. */
+ private void initCompatUi() {
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
+ }
+
+ // Construction extracted into the separate methods to allow injection for tests.
+ mViewHost = createSurfaceViewHost();
+ mCompatUILayout = inflateCompatUILayout();
+ mCompatUILayout.inject(this);
+
+ updateVisibilityOfViews();
+
+ mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
+ }
+
+ private void updateVisibilityOfViews() {
+ // Size Compat mode restart button.
+ mCompatUILayout.setRestartButtonVisibility(mHasSizeCompat);
+ if (mHasSizeCompat && mShouldShowSizeCompatHint) {
+ mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ // Only show by default for the first time.
+ mShouldShowSizeCompatHint = false;
+ }
+
+ // Camera control for stretched issues.
+ mCompatUILayout.setCameraControlVisibility(shouldShowCameraControl());
+ if (shouldShowCameraControl() && mShouldShowCameraCompatHint) {
+ mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ // Only show by default for the first time.
+ mShouldShowCameraCompatHint = false;
+ }
+ if (shouldShowCameraControl()) {
+ mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ }
+ }
+
+ private boolean shouldShowCameraControl() {
+ return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
+ && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
+ }
+
+ @VisibleForTesting
+ CompatUILayout inflateCompatUILayout() {
+ return (CompatUILayout) LayoutInflater.from(mContext)
+ .inflate(R.layout.compat_ui_layout, null);
+ }
+
+ @VisibleForTesting
+ SurfaceControlViewHost createSurfaceViewHost() {
+ return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ }
+
+ /** Gets the layout params. */
+ private WindowManager.LayoutParams getWindowLayoutParams() {
+ // Measure how big the hint is since its size depends on the text size.
+ mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
+ mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(),
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ winParams.token = new Binder();
+ winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId);
+ winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ return winParams;
+ }
+
+}
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 d239e56..6d4b2fa 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
@@ -54,8 +54,11 @@
import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.compatui.CompatUI;
+import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
@@ -74,8 +77,6 @@
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -156,33 +157,41 @@
@WMSingleton
@Provides
static DragAndDropController provideDragAndDropController(Context context,
- DisplayController displayController, UiEventLogger uiEventLogger) {
- return new DragAndDropController(context, displayController, uiEventLogger);
+ DisplayController displayController, UiEventLogger uiEventLogger,
+ IconProvider iconProvider, @ShellMainThread ShellExecutor mainExecutor) {
+ return new DragAndDropController(context, displayController, uiEventLogger, iconProvider,
+ mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static DragAndDrop provideDragAndDrop(DragAndDropController dragAndDropController) {
+ return dragAndDropController.asDragAndDrop();
}
@WMSingleton
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
Context context,
- SizeCompatUIController sizeCompatUI,
+ CompatUIController compatUI,
Optional<RecentTasksController> recentTasksOptional
) {
- return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional);
+ return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional);
}
@WMSingleton
@Provides
- static SizeCompatUI provideSizeCompatUI(SizeCompatUIController sizeCompatUIController) {
- return sizeCompatUIController.asSizeCompatUI();
+ static CompatUI provideCompatUI(CompatUIController compatUIController) {
+ return compatUIController.asCompatUI();
}
@WMSingleton
@Provides
- static SizeCompatUIController provideSizeCompatUIController(Context context,
+ static CompatUIController provideCompatUIController(Context context,
DisplayController displayController, DisplayInsetsController displayInsetsController,
DisplayImeController imeController, SyncTransactionQueue syncQueue,
@ShellMainThread ShellExecutor mainExecutor) {
- return new SizeCompatUIController(context, displayController, displayInsetsController,
+ return new CompatUIController(context, displayController, displayInsetsController,
imeController, syncQueue, mainExecutor);
}
@@ -337,7 +346,7 @@
@Provides
static Optional<OneHandedController> providesOneHandedController(
@DynamicOverride Optional<OneHandedController> oneHandedController) {
- if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ if (SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
return oneHandedController;
}
return Optional.empty();
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 46c7b50..f562fd9c 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
@@ -241,10 +241,11 @@
static PhonePipMenuController providesPipPhoneMenuController(Context context,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
SystemWindows systemWindows,
+ Optional<SplitScreenController> splitScreenOptional,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
- systemWindows, mainExecutor, mainHandler);
+ systemWindows, splitScreenOptional, mainExecutor, mainHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java
similarity index 60%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
copy to libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java
index a703114..edeff6e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,19 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.draganddrop;
+
+import android.content.res.Configuration;
import com.android.wm.shell.common.annotations.ExternalThread;
/**
- * Interface to engage size compat UI.
+ * Interface for telling DragAndDrop stuff.
*/
@ExternalThread
-public interface SizeCompatUI {
- /**
- * Called when the keyguard occluded state changes. Removes all size compat UIs if the
- * keyguard is now occluded.
- * @param occluded indicates if the keyguard is now occluded.
- */
- void onKeyguardOccludedChanged(boolean occluded);
+public interface DragAndDrop {
+
+ /** Called when the theme changes. */
+ void onThemeChanged();
+
+ /** Called when the configuration changes. */
+ void onConfigChanged(Configuration newConfig);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index d2b4711..101295d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -41,7 +41,6 @@
import android.graphics.PixelFormat;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.Display;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
@@ -53,8 +52,10 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -71,16 +72,26 @@
private final Context mContext;
private final DisplayController mDisplayController;
private final DragAndDropEventLogger mLogger;
+ private final IconProvider mIconProvider;
private SplitScreenController mSplitScreen;
+ private ShellExecutor mMainExecutor;
+ private DragAndDropImpl mImpl;
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
public DragAndDropController(Context context, DisplayController displayController,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger, IconProvider iconProvider, ShellExecutor mainExecutor) {
mContext = context;
mDisplayController = displayController;
mLogger = new DragAndDropEventLogger(uiEventLogger);
+ mIconProvider = iconProvider;
+ mMainExecutor = mainExecutor;
+ mImpl = new DragAndDropImpl();
+ }
+
+ public DragAndDrop asDragAndDrop() {
+ return mImpl;
}
public void initialize(Optional<SplitScreenController> splitscreen) {
@@ -117,7 +128,7 @@
R.layout.global_drop_target, null);
rootView.setOnDragListener(this);
rootView.setVisibility(View.INVISIBLE);
- DragLayout dragLayout = new DragLayout(context, mSplitScreen);
+ DragLayout dragLayout = new DragLayout(context, mSplitScreen, mIconProvider);
rootView.addView(dragLayout,
new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
try {
@@ -267,6 +278,18 @@
return mimeTypes;
}
+ private void onThemeChange() {
+ for (int i = 0; i < mDisplayDropTargets.size(); i++) {
+ mDisplayDropTargets.get(i).dragLayout.onThemeChange();
+ }
+ }
+
+ private void onConfigChanged(Configuration newConfig) {
+ for (int i = 0; i < mDisplayDropTargets.size(); i++) {
+ mDisplayDropTargets.get(i).dragLayout.onConfigChanged(newConfig);
+ }
+ }
+
private static class PerDisplay {
final int displayId;
final Context context;
@@ -287,4 +310,21 @@
dragLayout = dl;
}
}
+
+ private class DragAndDropImpl implements DragAndDrop {
+
+ @Override
+ public void onThemeChanged() {
+ mMainExecutor.execute(() -> {
+ DragAndDropController.this.onThemeChange();
+ });
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mMainExecutor.execute(() -> {
+ DragAndDropController.this.onConfigChanged(newConfig);
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index fbf04d6..8e6c05d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -29,16 +29,14 @@
import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.Intent.EXTRA_USER;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+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 static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -65,8 +63,7 @@
import com.android.internal.logging.InstanceId;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
-import com.android.wm.shell.splitscreen.SplitScreen.StageType;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
@@ -154,7 +151,13 @@
final Rect rightHitRegion = new Rect();
final Rect rightDrawRegion = bottomOrRightBounds;
- displayRegion.splitVertically(leftHitRegion, rightHitRegion);
+ // If we have existing split regions use those bounds, otherwise split it 50/50
+ if (inSplitScreen) {
+ leftHitRegion.set(topOrLeftBounds);
+ rightHitRegion.set(bottomOrRightBounds);
+ } else {
+ displayRegion.splitVertically(leftHitRegion, rightHitRegion);
+ }
mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
@@ -165,8 +168,13 @@
final Rect bottomHitRegion = new Rect();
final Rect bottomDrawRegion = bottomOrRightBounds;
- displayRegion.splitHorizontally(
- topHitRegion, bottomHitRegion);
+ // If we have existing split regions use those bounds, otherwise split it 50/50
+ if (inSplitScreen) {
+ topHitRegion.set(topOrLeftBounds);
+ bottomHitRegion.set(bottomOrRightBounds);
+ } else {
+ displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
+ }
mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
@@ -198,29 +206,23 @@
return;
}
- final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
- @StageType int stage = STAGE_TYPE_UNDEFINED;
@SplitPosition int position = SPLIT_POSITION_UNDEFINED;
if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
// Update launch options for the split side we are targeting.
position = leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
- if (!inSplitScreen) {
- // Launch in the side stage if we are not in split-screen already.
- stage = STAGE_TYPE_SIDE;
- }
// Add some data for logging splitscreen once it is invoked
mSplitScreen.logOnDroppedToSplit(position, mLoggerSessionId);
}
final ClipDescription description = data.getDescription();
final Intent dragData = mSession.dragData;
- startClipDescription(description, dragData, stage, position);
+ startClipDescription(description, dragData, position);
}
private void startClipDescription(ClipDescription description, Intent intent,
- @StageType int stage, @SplitPosition int position) {
+ @SplitPosition int position) {
final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
@@ -228,15 +230,15 @@
if (isTask) {
final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
- mStarter.startTask(taskId, stage, position, opts);
+ mStarter.startTask(taskId, position, opts);
} else if (isShortcut) {
final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
- mStarter.startShortcut(packageName, id, stage, position, opts, user);
+ mStarter.startShortcut(packageName, id, position, opts, user);
} else {
mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT),
- null, stage, position, opts);
+ null, position, opts);
}
}
@@ -291,12 +293,10 @@
* Interface for actually committing the task launches.
*/
public interface Starter {
- void startTask(int taskId, @StageType int stage, @SplitPosition int position,
- @Nullable Bundle options);
- void startShortcut(String packageName, String shortcutId, @StageType int stage,
- @SplitPosition int position, @Nullable Bundle options, UserHandle user);
- void startIntent(PendingIntent intent, Intent fillInIntent,
- @StageType int stage, @SplitPosition int position,
+ void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
+ @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
@@ -319,8 +319,7 @@
}
@Override
- public void startTask(int taskId, int stage, int position,
- @Nullable Bundle options) {
+ public void startTask(int taskId, int position, @Nullable Bundle options) {
try {
ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
} catch (RemoteException e) {
@@ -329,7 +328,7 @@
}
@Override
- public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ public void startShortcut(String packageName, String shortcutId, int position,
@Nullable Bundle options, UserHandle user) {
try {
LauncherApps launcherApps =
@@ -342,8 +341,8 @@
}
@Override
- public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int stage,
- int position, @Nullable Bundle options) {
+ public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int position,
+ @Nullable Bundle options) {
try {
intent.send(mContext, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index efc9ed0..fd3be2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -16,78 +16,143 @@
package com.android.wm.shell.draganddrop;
-import static com.android.wm.shell.animation.Interpolators.FAST_OUT_LINEAR_IN;
-import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.wm.shell.animation.Interpolators.LINEAR;
-import static com.android.wm.shell.animation.Interpolators.LINEAR_OUT_SLOW_IN;
+import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+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.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.StatusBarManager;
import android.content.ClipData;
import android.content.Context;
-import android.graphics.Canvas;
+import android.content.res.Configuration;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
import android.view.DragEvent;
import android.view.SurfaceControl;
-import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
-
-import androidx.annotation.NonNull;
+import android.widget.LinearLayout;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.ArrayList;
+import java.util.List;
/**
* Coordinates the visible drop targets for the current drag.
*/
-public class DragLayout extends View {
+public class DragLayout extends LinearLayout {
+
+ // While dragging the status bar is hidden.
+ private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS
+ | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+ | StatusBarManager.DISABLE_CLOCK
+ | StatusBarManager.DISABLE_SYSTEM_INFO;
private final DragAndDropPolicy mPolicy;
+ private final SplitScreenController mSplitScreenController;
+ private final IconProvider mIconProvider;
+ private final StatusBarManager mStatusBarManager;
private DragAndDropPolicy.Target mCurrentTarget = null;
- private DropOutlineDrawable mDropOutline;
+ private DropZoneView mDropZoneView1;
+ private DropZoneView mDropZoneView2;
+
private int mDisplayMargin;
+ private int mDividerSize;
private Insets mInsets = Insets.NONE;
private boolean mIsShowing;
private boolean mHasDropped;
- public DragLayout(Context context, SplitScreenController splitscreen) {
+ @SuppressLint("WrongConstant")
+ public DragLayout(Context context, SplitScreenController splitScreenController,
+ IconProvider iconProvider) {
super(context);
- mPolicy = new DragAndDropPolicy(context, splitscreen);
+ mSplitScreenController = splitScreenController;
+ mIconProvider = iconProvider;
+ mPolicy = new DragAndDropPolicy(context, splitScreenController);
+ mStatusBarManager = context.getSystemService(StatusBarManager.class);
+
mDisplayMargin = context.getResources().getDimensionPixelSize(
R.dimen.drop_layout_display_margin);
- mDropOutline = new DropOutlineDrawable(context);
- setBackground(mDropOutline);
- setWillNotDraw(false);
+ mDividerSize = context.getResources().getDimensionPixelSize(
+ R.dimen.split_divider_bar_width);
+
+ mDropZoneView1 = new DropZoneView(context);
+ mDropZoneView2 = new DropZoneView(context);
+ addView(mDropZoneView1, new LinearLayout.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT));
+ addView(mDropZoneView2, new LinearLayout.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT));
+ ((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1;
+ ((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1;
+ updateContainerMargins();
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mInsets = insets.getInsets(Type.systemBars() | Type.displayCutout());
recomputeDropTargets();
+
+ final int orientation = getResources().getConfiguration().orientation;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ mDropZoneView1.setBottomInset(mInsets.bottom);
+ mDropZoneView2.setBottomInset(mInsets.bottom);
+ } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ mDropZoneView1.setBottomInset(0);
+ mDropZoneView2.setBottomInset(mInsets.bottom);
+ }
return super.onApplyWindowInsets(insets);
}
- @Override
- protected boolean verifyDrawable(@NonNull Drawable who) {
- return who == mDropOutline || super.verifyDrawable(who);
+ public void onThemeChange() {
+ mDropZoneView1.onThemeChange();
+ mDropZoneView2.onThemeChange();
}
- @Override
- protected void onDraw(Canvas canvas) {
- if (mCurrentTarget != null) {
- mDropOutline.draw(canvas);
+ public void onConfigChanged(Configuration newConfig) {
+ final int orientation = getResources().getConfiguration().orientation;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE
+ && getOrientation() != HORIZONTAL) {
+ setOrientation(LinearLayout.HORIZONTAL);
+ updateContainerMargins();
+ } else if (orientation == Configuration.ORIENTATION_PORTRAIT
+ && getOrientation() != VERTICAL) {
+ setOrientation(LinearLayout.VERTICAL);
+ updateContainerMargins();
+ }
+ }
+
+ private void updateContainerMargins() {
+ final int orientation = getResources().getConfiguration().orientation;
+ final float halfMargin = mDisplayMargin / 2f;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ mDropZoneView1.setContainerMargin(
+ mDisplayMargin, mDisplayMargin, halfMargin, mDisplayMargin);
+ mDropZoneView2.setContainerMargin(
+ halfMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin);
+ } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ mDropZoneView1.setContainerMargin(
+ mDisplayMargin, mDisplayMargin, mDisplayMargin, halfMargin);
+ mDropZoneView2.setContainerMargin(
+ mDisplayMargin, halfMargin, mDisplayMargin, mDisplayMargin);
}
}
@@ -104,6 +169,81 @@
mPolicy.start(displayLayout, initialData, loggerSessionId);
mHasDropped = false;
mCurrentTarget = null;
+
+ boolean alreadyInSplit = mSplitScreenController != null
+ && mSplitScreenController.isSplitScreenVisible();
+ if (!alreadyInSplit) {
+ List<ActivityManager.RunningTaskInfo> tasks = null;
+ // Figure out the splashscreen info for the existing task.
+ try {
+ tasks = ActivityTaskManager.getService().getTasks(1,
+ false /* filterOnlyVisibleRecents */,
+ false /* keepIntentExtra */);
+ } catch (RemoteException e) {
+ // don't show an icon / will just use the defaults
+ }
+ if (tasks != null && !tasks.isEmpty()) {
+ ActivityManager.RunningTaskInfo taskInfo1 = tasks.get(0);
+ Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
+ int bgColor1 = getResizingBackgroundColor(taskInfo1);
+ mDropZoneView1.setAppInfo(bgColor1, icon1);
+ mDropZoneView2.setAppInfo(bgColor1, icon1);
+ updateDropZoneSizes(null, null); // passing null splits the views evenly
+ }
+ } else {
+ // We're already in split so get taskInfo from the controller to populate icon / color.
+ ActivityManager.RunningTaskInfo topOrLeftTask =
+ mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ ActivityManager.RunningTaskInfo bottomOrRightTask =
+ mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ if (topOrLeftTask != null && bottomOrRightTask != null) {
+ Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo);
+ int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask);
+ Drawable bottomOrRightIcon = mIconProvider.getIcon(
+ bottomOrRightTask.topActivityInfo);
+ int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask);
+ mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon);
+ mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon);
+ }
+
+ // Update the dropzones to match existing split sizes
+ Rect topOrLeftBounds = new Rect();
+ Rect bottomOrRightBounds = new Rect();
+ mSplitScreenController.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
+ updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds);
+ }
+ }
+
+ /**
+ * Sets the size of the two drop zones based on the provided bounds. The divider sits between
+ * the views and its size is included in the calculations.
+ *
+ * @param bounds1 bounds to apply to the first dropzone view, null if split in half.
+ * @param bounds2 bounds to apply to the second dropzone view, null if split in half.
+ */
+ private void updateDropZoneSizes(Rect bounds1, Rect bounds2) {
+ final int orientation = getResources().getConfiguration().orientation;
+ final boolean isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT;
+ final int halfDivider = mDividerSize / 2;
+ final LinearLayout.LayoutParams dropZoneView1 =
+ (LayoutParams) mDropZoneView1.getLayoutParams();
+ final LinearLayout.LayoutParams dropZoneView2 =
+ (LayoutParams) mDropZoneView2.getLayoutParams();
+ if (isPortrait) {
+ dropZoneView1.width = MATCH_PARENT;
+ dropZoneView2.width = MATCH_PARENT;
+ dropZoneView1.height = bounds1 != null ? bounds1.height() + halfDivider : MATCH_PARENT;
+ dropZoneView2.height = bounds2 != null ? bounds2.height() + halfDivider : MATCH_PARENT;
+ } else {
+ dropZoneView1.width = bounds1 != null ? bounds1.width() + halfDivider : MATCH_PARENT;
+ dropZoneView2.width = bounds2 != null ? bounds2.width() + halfDivider : MATCH_PARENT;
+ dropZoneView1.height = MATCH_PARENT;
+ dropZoneView2.height = MATCH_PARENT;
+ }
+ dropZoneView1.weight = bounds1 != null ? 0 : 1;
+ dropZoneView2.weight = bounds2 != null ? 0 : 1;
+ mDropZoneView1.setLayoutParams(dropZoneView1);
+ mDropZoneView2.setLayoutParams(dropZoneView2);
}
public void show() {
@@ -139,20 +279,14 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Current target: %s", target);
if (target == null) {
// Animating to no target
- mDropOutline.startVisibilityAnimation(false, LINEAR);
- Rect finalBounds = new Rect(mCurrentTarget.drawRegion);
- finalBounds.inset(mDisplayMargin, mDisplayMargin);
- mDropOutline.startBoundsAnimation(finalBounds, FAST_OUT_LINEAR_IN);
+ animateSplitContainers(false, null /* animCompleteCallback */);
} else if (mCurrentTarget == null) {
// Animating to first target
- mDropOutline.startVisibilityAnimation(true, LINEAR);
- Rect initialBounds = new Rect(target.drawRegion);
- initialBounds.inset(mDisplayMargin, mDisplayMargin);
- mDropOutline.setRegionBounds(initialBounds);
- mDropOutline.startBoundsAnimation(target.drawRegion, LINEAR_OUT_SLOW_IN);
+ animateSplitContainers(true, null /* animCompleteCallback */);
+ animateHighlight(target);
} else {
- // Bounds change
- mDropOutline.startBoundsAnimation(target.drawRegion, FAST_OUT_SLOW_IN);
+ // Switching between targets
+ animateHighlight(target);
}
mCurrentTarget = target;
}
@@ -163,26 +297,7 @@
*/
public void hide(DragEvent event, Runnable hideCompleteCallback) {
mIsShowing = false;
- ObjectAnimator alphaAnimator = mDropOutline.startVisibilityAnimation(false, LINEAR);
- ObjectAnimator boundsAnimator = null;
- if (mCurrentTarget != null) {
- Rect finalBounds = new Rect(mCurrentTarget.drawRegion);
- finalBounds.inset(mDisplayMargin, mDisplayMargin);
- boundsAnimator = mDropOutline.startBoundsAnimation(finalBounds, FAST_OUT_LINEAR_IN);
- }
-
- if (hideCompleteCallback != null) {
- ObjectAnimator lastAnim = boundsAnimator != null
- ? boundsAnimator
- : alphaAnimator;
- lastAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- hideCompleteCallback.run();
- }
- });
- }
-
+ animateSplitContainers(false, hideCompleteCallback);
mCurrentTarget = null;
}
@@ -201,4 +316,49 @@
hide(event, dropCompleteCallback);
return handledDrop;
}
+
+ private void animateSplitContainers(boolean visible, Runnable animCompleteCallback) {
+ mStatusBarManager.disable(visible
+ ? HIDE_STATUS_BAR_FLAGS
+ : DISABLE_NONE);
+ mDropZoneView1.setShowingMargin(visible);
+ mDropZoneView2.setShowingMargin(visible);
+ ObjectAnimator animator = mDropZoneView1.getAnimator();
+ if (animCompleteCallback != null) {
+ if (animator != null) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animCompleteCallback.run();
+ }
+ });
+ } else {
+ // If there's no animator the animation is done so run immediately
+ animCompleteCallback.run();
+ }
+ }
+ }
+
+ private void animateHighlight(DragAndDropPolicy.Target target) {
+ if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_LEFT
+ || target.type == DragAndDropPolicy.Target.TYPE_SPLIT_TOP) {
+ mDropZoneView1.setShowingHighlight(true);
+ mDropZoneView1.setShowingSplash(false);
+
+ mDropZoneView2.setShowingHighlight(false);
+ mDropZoneView2.setShowingSplash(true);
+ } else if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT
+ || target.type == DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM) {
+ mDropZoneView1.setShowingHighlight(false);
+ mDropZoneView1.setShowingSplash(true);
+
+ mDropZoneView2.setShowingHighlight(true);
+ mDropZoneView2.setShowingSplash(false);
+ }
+ }
+
+ private static int getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
new file mode 100644
index 0000000..2f47af5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -0,0 +1,313 @@
+/*
+ * 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.wm.shell.draganddrop;
+
+import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Path;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.IntProperty;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.wm.shell.R;
+
+/**
+ * Renders a drop zone area for items being dragged.
+ */
+public class DropZoneView extends FrameLayout {
+
+ private static final int SPLASHSCREEN_ALPHA_INT = (int) (255 * 0.90f);
+ private static final int HIGHLIGHT_ALPHA_INT = 255;
+ private static final int MARGIN_ANIMATION_ENTER_DURATION = 400;
+ private static final int MARGIN_ANIMATION_EXIT_DURATION = 250;
+
+ private static final FloatProperty<DropZoneView> INSETS =
+ new FloatProperty<DropZoneView>("insets") {
+ @Override
+ public void setValue(DropZoneView v, float percent) {
+ v.setMarginPercent(percent);
+ }
+
+ @Override
+ public Float get(DropZoneView v) {
+ return v.getMarginPercent();
+ }
+ };
+
+ private static final IntProperty<ColorDrawable> SPLASHSCREEN_ALPHA =
+ new IntProperty<ColorDrawable>("splashscreen") {
+ @Override
+ public void setValue(ColorDrawable d, int alpha) {
+ d.setAlpha(alpha);
+ }
+
+ @Override
+ public Integer get(ColorDrawable d) {
+ return d.getAlpha();
+ }
+ };
+
+ private static final IntProperty<ColorDrawable> HIGHLIGHT_ALPHA =
+ new IntProperty<ColorDrawable>("highlight") {
+ @Override
+ public void setValue(ColorDrawable d, int alpha) {
+ d.setAlpha(alpha);
+ }
+
+ @Override
+ public Integer get(ColorDrawable d) {
+ return d.getAlpha();
+ }
+ };
+
+ private final Path mPath = new Path();
+ private final float[] mContainerMargin = new float[4];
+ private float mCornerRadius;
+ private float mBottomInset;
+ private int mMarginColor; // i.e. color used for negative space like the container insets
+ private int mHighlightColor;
+
+ private boolean mShowingHighlight;
+ private boolean mShowingSplash;
+ private boolean mShowingMargin;
+
+ // TODO: might be more seamless to animate between splash/highlight color instead of 2 separate
+ private ObjectAnimator mSplashAnimator;
+ private ObjectAnimator mHighlightAnimator;
+ private ObjectAnimator mMarginAnimator;
+ private float mMarginPercent;
+
+ // Renders a highlight or neutral transparent color
+ private ColorDrawable mDropZoneDrawable;
+ // Renders the translucent splashscreen with the app icon in the middle
+ private ImageView mSplashScreenView;
+ private ColorDrawable mSplashBackgroundDrawable;
+ // Renders the margin / insets around the dropzone container
+ private MarginView mMarginView;
+
+ public DropZoneView(Context context) {
+ this(context, null);
+ }
+
+ public DropZoneView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DropZoneView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DropZoneView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setContainerMargin(0, 0, 0, 0); // make sure it's populated
+
+ mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mMarginColor = getResources().getColor(R.color.taskbar_background);
+ mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
+
+ mDropZoneDrawable = new ColorDrawable();
+ mDropZoneDrawable.setColor(mHighlightColor);
+ mDropZoneDrawable.setAlpha(0);
+ setBackgroundDrawable(mDropZoneDrawable);
+
+ mSplashScreenView = new ImageView(context);
+ mSplashScreenView.setScaleType(ImageView.ScaleType.CENTER);
+ mSplashBackgroundDrawable = new ColorDrawable();
+ mSplashBackgroundDrawable.setColor(Color.WHITE);
+ mSplashBackgroundDrawable.setAlpha(SPLASHSCREEN_ALPHA_INT);
+ mSplashScreenView.setBackgroundDrawable(mSplashBackgroundDrawable);
+ addView(mSplashScreenView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mSplashScreenView.setAlpha(0f);
+
+ mMarginView = new MarginView(context);
+ addView(mMarginView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ }
+
+ public void onThemeChange() {
+ mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(getContext());
+ mMarginColor = getResources().getColor(R.color.taskbar_background);
+ mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
+
+ final int alpha = mDropZoneDrawable.getAlpha();
+ mDropZoneDrawable.setColor(mHighlightColor);
+ mDropZoneDrawable.setAlpha(alpha);
+
+ if (mMarginPercent > 0) {
+ mMarginView.invalidate();
+ }
+ }
+
+ /** Sets the desired margins around the drop zone container when fully showing. */
+ public void setContainerMargin(float left, float top, float right, float bottom) {
+ mContainerMargin[0] = left;
+ mContainerMargin[1] = top;
+ mContainerMargin[2] = right;
+ mContainerMargin[3] = bottom;
+ if (mMarginPercent > 0) {
+ mMarginView.invalidate();
+ }
+ }
+
+ /** Sets the bottom inset so the drop zones are above bottom navigation. */
+ public void setBottomInset(float bottom) {
+ mBottomInset = bottom;
+ ((LayoutParams) mSplashScreenView.getLayoutParams()).bottomMargin = (int) bottom;
+ if (mMarginPercent > 0) {
+ mMarginView.invalidate();
+ }
+ }
+
+ /** Sets the color and icon to use for the splashscreen when shown. */
+ public void setAppInfo(int splashScreenColor, Drawable appIcon) {
+ mSplashBackgroundDrawable.setColor(splashScreenColor);
+ mSplashScreenView.setImageDrawable(appIcon);
+ }
+
+ /** @return an active animator for this view if one exists. */
+ @Nullable
+ public ObjectAnimator getAnimator() {
+ if (mMarginAnimator != null && mMarginAnimator.isRunning()) {
+ return mMarginAnimator;
+ } else if (mHighlightAnimator != null && mHighlightAnimator.isRunning()) {
+ return mHighlightAnimator;
+ } else if (mSplashAnimator != null && mSplashAnimator.isRunning()) {
+ return mSplashAnimator;
+ }
+ return null;
+ }
+
+ /** Animates the splashscreen to show or hide. */
+ public void setShowingSplash(boolean showingSplash) {
+ if (mShowingSplash != showingSplash) {
+ mShowingSplash = showingSplash;
+ animateSplashToState();
+ }
+ }
+
+ /** Animates the highlight indicating the zone is hovered on or not. */
+ public void setShowingHighlight(boolean showingHighlight) {
+ if (mShowingHighlight != showingHighlight) {
+ mShowingHighlight = showingHighlight;
+ animateHighlightToState();
+ }
+ }
+
+ /** Animates the margins around the drop zone to show or hide. */
+ public void setShowingMargin(boolean visible) {
+ if (mShowingMargin != visible) {
+ mShowingMargin = visible;
+ animateMarginToState();
+ }
+ if (!mShowingMargin) {
+ setShowingHighlight(false);
+ setShowingSplash(false);
+ }
+ }
+
+ private void animateSplashToState() {
+ if (mSplashAnimator != null) {
+ mSplashAnimator.cancel();
+ }
+ mSplashAnimator = ObjectAnimator.ofInt(mSplashBackgroundDrawable,
+ SPLASHSCREEN_ALPHA,
+ mSplashBackgroundDrawable.getAlpha(),
+ mShowingSplash ? SPLASHSCREEN_ALPHA_INT : 0);
+ if (!mShowingSplash) {
+ mSplashAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ }
+ mSplashAnimator.start();
+ mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start();
+ }
+
+ private void animateHighlightToState() {
+ if (mHighlightAnimator != null) {
+ mHighlightAnimator.cancel();
+ }
+ mHighlightAnimator = ObjectAnimator.ofInt(mDropZoneDrawable,
+ HIGHLIGHT_ALPHA,
+ mDropZoneDrawable.getAlpha(),
+ mShowingHighlight ? HIGHLIGHT_ALPHA_INT : 0);
+ if (!mShowingHighlight) {
+ mHighlightAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ }
+ mHighlightAnimator.start();
+ }
+
+ private void animateMarginToState() {
+ if (mMarginAnimator != null) {
+ mMarginAnimator.cancel();
+ }
+ mMarginAnimator = ObjectAnimator.ofFloat(this, INSETS,
+ mMarginPercent,
+ mShowingMargin ? 1f : 0f);
+ mMarginAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ mMarginAnimator.setDuration(mShowingMargin
+ ? MARGIN_ANIMATION_ENTER_DURATION
+ : MARGIN_ANIMATION_EXIT_DURATION);
+ mMarginAnimator.start();
+ }
+
+ private void setMarginPercent(float percent) {
+ if (percent != mMarginPercent) {
+ mMarginPercent = percent;
+ mMarginView.invalidate();
+ }
+ }
+
+ private float getMarginPercent() {
+ return mMarginPercent;
+ }
+
+ /** Simple view that draws a rounded rect margin around its contents. **/
+ private class MarginView extends View {
+
+ MarginView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ mPath.reset();
+ mPath.addRoundRect(mContainerMargin[0] * mMarginPercent,
+ mContainerMargin[1] * mMarginPercent,
+ getWidth() - (mContainerMargin[2] * mMarginPercent),
+ getHeight() - (mContainerMargin[3] * mMarginPercent) - mBottomInset,
+ mCornerRadius * mMarginPercent,
+ mCornerRadius * mMarginPercent,
+ Path.Direction.CW);
+ mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
+ canvas.clipPath(mPath);
+ canvas.drawColor(mMarginColor);
+ }
+ }
+}
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 5c8e7d0..52ff21b 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.freeform;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
@@ -28,7 +27,6 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.SurfaceControl;
-import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -85,13 +83,6 @@
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
return;
}
-
- // Clears windowing mode and window bounds to let the task inherits from its new parent.
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(taskInfo.token, null)
- .setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
- mSyncQueue.queue(wct);
-
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OWNERS
new file mode 100644
index 0000000..41177f0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module one handed mode owner
+lbill@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
new file mode 100644
index 0000000..afddfab
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module pip owner
+hwwang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 6d4773b..c0734e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -112,11 +112,17 @@
default void showPictureInPictureMenu() {}
/**
- * Called by NavigationBar in order to listen in for PiP bounds change. This is mostly used
- * for times where the PiP bounds could conflict with SystemUI elements, such as a stashed
- * PiP and the Back-from-Edge gesture.
+ * Called by NavigationBar and TaskbarDelegate in order to listen in for PiP bounds change. This
+ * is mostly used for times where the PiP bounds could conflict with SystemUI elements, such as
+ * a stashed PiP and the Back-from-Edge gesture.
*/
- default void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
+ default void addPipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
+
+ /**
+ * Remove a callback added previously. This is used when NavigationBar is removed from the
+ * view hierarchy or destroyed.
+ */
+ default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
/**
* Dump the current state and information if need.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index e3674dc..b3558ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -38,6 +38,8 @@
import java.io.PrintWriter;
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.Consumer;
@@ -89,7 +91,7 @@
private @Nullable Runnable mOnMinimalSizeChangeCallback;
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
- private @Nullable Consumer<Rect> mOnPipExclusionBoundsChangeCallback;
+ private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
public PipBoundsState(@NonNull Context context) {
mContext = context;
@@ -108,8 +110,8 @@
/** Set the current PIP bounds. */
public void setBounds(@NonNull Rect bounds) {
mBounds.set(bounds);
- if (mOnPipExclusionBoundsChangeCallback != null) {
- mOnPipExclusionBoundsChangeCallback.accept(bounds);
+ for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) {
+ callback.accept(bounds);
}
}
@@ -407,17 +409,25 @@
}
/**
- * Set a callback to watch out for PiP bounds. This is mostly used by SystemUI's
+ * Add a callback to watch out for PiP bounds. This is mostly used by SystemUI's
* Back-gesture handler, to avoid conflicting with PiP when it's stashed.
*/
- public void setPipExclusionBoundsChangeCallback(
+ public void addPipExclusionBoundsChangeCallback(
@Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
- mOnPipExclusionBoundsChangeCallback = onPipExclusionBoundsChangeCallback;
- if (mOnPipExclusionBoundsChangeCallback != null) {
- mOnPipExclusionBoundsChangeCallback.accept(getBounds());
+ mOnPipExclusionBoundsChangeCallbacks.add(onPipExclusionBoundsChangeCallback);
+ for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) {
+ callback.accept(getBounds());
}
}
+ /**
+ * Remove a callback that was previously added.
+ */
+ public void removePipExclusionBoundsChangeCallback(
+ @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
+ mOnPipExclusionBoundsChangeCallbacks.remove(onPipExclusionBoundsChangeCallback);
+ }
+
/** Source of truth for the current bounds of PIP that may be in motion. */
public static class MotionBoundsState {
/** The bounds used when PIP is in motion (e.g. during a drag or animation) */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index 8d9ad4d..caa1f01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -90,6 +90,11 @@
default void updateMenuBounds(Rect destinationBounds) {}
/**
+ * Update when the current focused task changes.
+ */
+ default void onFocusTaskChanged(RunningTaskInfo taskInfo) {}
+
+ /**
* Returns a default LayoutParams for the PIP Menu.
* @param width the PIP stack width.
* @param height the PIP stack height.
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 6cc5f09..f0b2716 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
@@ -98,7 +98,7 @@
* see also {@link PipMotionHelper}.
*/
public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
- DisplayController.OnDisplaysChangedListener {
+ DisplayController.OnDisplaysChangedListener, ShellTaskOrganizer.FocusListener {
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
private static final boolean DEBUG = false;
/**
@@ -286,6 +286,7 @@
mMainExecutor.execute(() -> {
mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
});
+ mTaskOrganizer.addFocusListener(this);
mPipTransitionController.setPipOrganizer(this);
displayController.addDisplayWindowListener(this);
}
@@ -772,8 +773,13 @@
}
@Override
- public boolean supportSizeCompatUI() {
- // PIP doesn't support size compat.
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mPipMenuController.onFocusTaskChanged(taskInfo);
+ }
+
+ @Override
+ public boolean supportCompatUI() {
+ // PIP doesn't support compat.
return false;
}
@@ -1249,11 +1255,7 @@
} else if (isOutPipDirection(direction)) {
// If we are animating to fullscreen or split screen, then we need to reset the
// override bounds on the task to ensure that the task "matches" the parent's bounds.
- if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
- taskBounds = destinationBounds;
- } else {
- taskBounds = null;
- }
+ taskBounds = null;
applyWindowingModeChangeOnExit(wct, direction);
} else {
// Just a resize in PIP
@@ -1282,6 +1284,9 @@
}
private boolean isPipTopLeft() {
+ if (!mSplitScreenOptional.isPresent()) {
+ return false;
+ }
final Rect topLeft = new Rect();
final Rect bottomRight = new Rect();
mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 5687f4d..101a55d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.RemoteAction;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -43,10 +44,12 @@
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Manages the PiP menu view which can show menu options or a scrim.
@@ -114,6 +117,7 @@
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final SystemWindows mSystemWindows;
+ private final Optional<SplitScreenController> mSplitScreenController;
private ParceledListSlice<RemoteAction> mAppActions;
private ParceledListSlice<RemoteAction> mMediaActions;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -145,6 +149,7 @@
public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
PipMediaController mediaController, SystemWindows systemWindows,
+ Optional<SplitScreenController> splitScreenOptional,
ShellExecutor mainExecutor, Handler mainHandler) {
mContext = context;
mPipBoundsState = pipBoundsState;
@@ -152,6 +157,7 @@
mSystemWindows = systemWindows;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mSplitScreenController = splitScreenOptional;
}
public boolean isMenuVisible() {
@@ -180,10 +186,12 @@
if (mPipMenuView != null) {
detachPipMenuView();
}
- mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler);
+ mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
+ mSplitScreenController);
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
+ setShellRootAccessibilityWindow();
}
private void detachPipMenuView() {
@@ -209,6 +217,13 @@
updateMenuLayout(destinationBounds);
}
+ @Override
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mPipMenuView != null) {
+ mPipMenuView.onFocusTaskChanged(taskInfo);
+ }
+ }
+
/**
* Tries to grab a surface control from {@link PipMenuView}. If this isn't available for some
* reason (ie. the window isn't ready yet, thus {@link android.view.ViewRootImpl} is
@@ -532,6 +547,10 @@
mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState));
}
mMenuState = menuState;
+ setShellRootAccessibilityWindow();
+ }
+
+ private void setShellRootAccessibilityWindow() {
switch (mMenuState) {
case MENU_STATE_NONE:
mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, null);
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 26fd962..a41fd84 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
@@ -843,9 +843,16 @@
}
@Override
- public void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) {
+ public void addPipExclusionBoundsChangeListener(Consumer<Rect> listener) {
mMainExecutor.execute(() -> {
- mPipBoundsState.setPipExclusionBoundsChangeCallback(listener);
+ mPipBoundsState.addPipExclusionBoundsChangeCallback(listener);
+ });
+ }
+
+ @Override
+ public void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) {
+ mMainExecutor.execute(() -> {
+ mPipBoundsState.removePipExclusionBoundsChangeCallback(listener);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 0fbdf90..92a3598 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -120,6 +120,11 @@
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
+ if (mTargetViewContainer != null) {
+ // init can be called multiple times, remove the old one from view hierarchy first.
+ cleanUpDismissTarget();
+ }
+
mTargetView = new DismissCircleView(mContext);
mTargetViewContainer = new FrameLayout(mContext);
mTargetViewContainer.setBackgroundDrawable(
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 b209699..da4bbe8 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip.phone;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
@@ -32,8 +33,10 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.app.ActivityManager;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -61,11 +64,13 @@
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Translucent window that gets started on top of a task in PIP to allow the user to control it.
@@ -105,6 +110,7 @@
private boolean mAllowMenuTimeout = true;
private boolean mAllowTouches = true;
private int mDismissFadeOutDurationMs;
+ private boolean mFocusedTaskAllowSplitScreen;
private final List<RemoteAction> mActions = new ArrayList<>();
@@ -116,6 +122,7 @@
private AnimatorSet mMenuContainerAnimator;
private PhonePipMenuController mController;
+ private Optional<SplitScreenController> mSplitScreenControllerOptional;
private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -144,12 +151,14 @@
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
public PipMenuView(Context context, PhonePipMenuController controller,
- ShellExecutor mainExecutor, Handler mainHandler) {
+ ShellExecutor mainExecutor, Handler mainHandler,
+ Optional<SplitScreenController> splitScreenController) {
super(context, null, 0);
mContext = context;
mController = controller;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mSplitScreenControllerOptional = splitScreenController;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
@@ -180,7 +189,7 @@
mEnterSplitButton = findViewById(R.id.enter_split);
mEnterSplitButton.setAlpha(0);
mEnterSplitButton.setOnClickListener(v -> {
- if (mMenuContainer.getAlpha() != 0) {
+ if (mEnterSplitButton.getAlpha() != 0) {
enterSplit();
}
});
@@ -255,6 +264,15 @@
return super.dispatchGenericMotionEvent(event);
}
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ final boolean isSplitScreen = mSplitScreenControllerOptional.isPresent()
+ && mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId);
+ mFocusedTaskAllowSplitScreen = isSplitScreen
+ || (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && taskInfo.supportsSplitScreenMultiWindow
+ && taskInfo.topActivityType != WindowConfiguration.ACTIVITY_TYPE_HOME);
+ }
+
void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
mAllowMenuTimeout = allowMenuTimeout;
@@ -278,7 +296,8 @@
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f);
ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
- mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
+ mEnterSplitButton.getAlpha(),
+ ENABLE_ENTER_SPLIT && mFocusedTaskAllowSplitScreen ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
enterSplitAnim);
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 7cf3baf..7decb54 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
@@ -25,6 +25,7 @@
import android.app.TaskInfo;
import android.content.Context;
import android.os.RemoteException;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -111,6 +112,11 @@
if (taskId1 == taskId2) {
return;
}
+ if (mSplitTasks.get(taskId1, INVALID_TASK_ID) == taskId2
+ && mTaskSplitBoundsMap.get(taskId1).equals(splitBounds)) {
+ // If the two tasks are already paired and the bounds are the same, then skip updating
+ return;
+ }
// Remove any previous pairs
removeSplitPair(taskId1);
removeSplitPair(taskId2);
@@ -121,6 +127,7 @@
mSplitTasks.put(taskId2, taskId1);
mTaskSplitBoundsMap.put(taskId1, splitBounds);
mTaskSplitBoundsMap.put(taskId2, splitBounds);
+ notifyRecentTasksChanged();
}
/**
@@ -133,6 +140,7 @@
mSplitTasks.delete(pairedTaskId);
mTaskSplitBoundsMap.remove(taskId);
mTaskSplitBoundsMap.remove(pairedTaskId);
+ notifyRecentTasksChanged();
}
}
@@ -217,7 +225,7 @@
}
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
- if (pairedTaskId != INVALID_TASK_ID) {
+ if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(pairedTaskId)) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
@@ -284,6 +292,7 @@
* Invalidates this instance, preventing future calls from updating the controller.
*/
void invalidate() {
+ Slog.d("b/206648922", "invalidating controller: " + mController);
mController = null;
}
@@ -309,6 +318,8 @@
(controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
.toArray(new GroupedRecentTaskInfo[0]),
true /* blocking */);
+ Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0]
+ + " mController=" + mController);
return out[0];
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
deleted file mode 100644
index ff6f913..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Popup to show the hint about the {@link SizeCompatRestartButton}. */
-public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener {
-
- private SizeCompatUILayout mLayout;
-
- public SizeCompatHintPopup(Context context) {
- super(context);
- }
-
- public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- void inject(SizeCompatUILayout layout) {
- mLayout = layout;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- final LinearLayout hintPopup = findViewById(R.id.size_compat_hint_popup);
- hintPopup.setOnClickListener(this);
- }
-
- @Override
- public void onClick(View v) {
- mLayout.dismissHint();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
deleted file mode 100644
index d75fe517..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Button to restart the size compat activity. */
-public class SizeCompatRestartButton extends FrameLayout implements View.OnClickListener,
- View.OnLongClickListener {
-
- private SizeCompatUILayout mLayout;
-
- public SizeCompatRestartButton(@NonNull Context context) {
- super(context);
- }
-
- public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- void inject(SizeCompatUILayout layout) {
- mLayout = layout;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
- restartButton.setOnClickListener(this);
- restartButton.setOnLongClickListener(this);
- }
-
- @Override
- public void onClick(View v) {
- mLayout.onRestartButtonClicked();
- }
-
- @Override
- public boolean onLongClick(View v) {
- mLayout.onRestartButtonLongClicked();
- return true;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
deleted file mode 100644
index c35b89a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-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_APPLICATION_OVERLAY;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.util.Log;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-/**
- * Records and handles layout of size compat UI on a task with size compat activity. Helps to
- * calculate proper bounds when configuration or UI position changes.
- */
-class SizeCompatUILayout {
- private static final String TAG = "SizeCompatUILayout";
-
- final SyncTransactionQueue mSyncQueue;
- private final SizeCompatUIController.SizeCompatUICallback mCallback;
- private Context mContext;
- private Configuration mTaskConfig;
- private final int mDisplayId;
- private final int mTaskId;
- private ShellTaskOrganizer.TaskListener mTaskListener;
- private DisplayLayout mDisplayLayout;
- private final Rect mStableBounds;
- private final int mButtonWidth;
- private final int mButtonHeight;
- private final int mPopupOffsetX;
- private final int mPopupOffsetY;
-
- @VisibleForTesting
- final SizeCompatUIWindowManager mButtonWindowManager;
- @VisibleForTesting
- @Nullable
- SizeCompatUIWindowManager mHintWindowManager;
- @VisibleForTesting
- @Nullable
- SizeCompatRestartButton mButton;
- @VisibleForTesting
- @Nullable
- SizeCompatHintPopup mHint;
- @VisibleForTesting
- boolean mShouldShowHint;
-
- SizeCompatUILayout(SyncTransactionQueue syncQueue,
- SizeCompatUIController.SizeCompatUICallback callback, Context context,
- Configuration taskConfig, int taskId, ShellTaskOrganizer.TaskListener taskListener,
- DisplayLayout displayLayout, boolean hasShownHint) {
- mSyncQueue = syncQueue;
- mCallback = callback;
- mContext = context.createConfigurationContext(taskConfig);
- mTaskConfig = taskConfig;
- mDisplayId = mContext.getDisplayId();
- mTaskId = taskId;
- mTaskListener = taskListener;
- mDisplayLayout = displayLayout;
- mShouldShowHint = !hasShownHint;
- mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
-
- mStableBounds = new Rect();
- mDisplayLayout.getStableBounds(mStableBounds);
-
- final Resources resources = mContext.getResources();
- mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
- mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
- mPopupOffsetX = (mButtonWidth / 2) - resources.getDimensionPixelSize(
- R.dimen.size_compat_hint_corner_radius) - (resources.getDimensionPixelSize(
- R.dimen.size_compat_hint_point_width) / 2);
- mPopupOffsetY = mButtonHeight;
- }
-
- /** Creates the activity restart button window. */
- void createSizeCompatButton(boolean show) {
- if (!show || mButton != null) {
- // Wait until button should be visible.
- return;
- }
- mButton = mButtonWindowManager.createSizeCompatButton();
- updateButtonSurfacePosition();
-
- if (mShouldShowHint) {
- // Only show by default for the first time.
- mShouldShowHint = false;
- createSizeCompatHint();
- }
-
- mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
- }
-
- /** Creates the restart button hint window. */
- private void createSizeCompatHint() {
- if (mHint != null) {
- // Hint already shown.
- return;
- }
- mHintWindowManager = createHintWindowManager();
- mHint = mHintWindowManager.createSizeCompatHint();
- updateHintSurfacePosition();
- }
-
- @VisibleForTesting
- SizeCompatUIWindowManager createHintWindowManager() {
- return new SizeCompatUIWindowManager(mContext, mTaskConfig, this);
- }
-
- /** Dismisses the hint window. */
- void dismissHint() {
- mHint = null;
- if (mHintWindowManager != null) {
- mHintWindowManager.release();
- mHintWindowManager = null;
- }
- }
-
- /** Releases the UI windows. */
- void release() {
- dismissHint();
- mButton = null;
- mButtonWindowManager.release();
- }
-
- /** Called when size compat info changed. */
- void updateSizeCompatInfo(Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener, boolean show) {
- final Configuration prevTaskConfig = mTaskConfig;
- final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
- mTaskConfig = taskConfig;
- mTaskListener = taskListener;
-
- // Update configuration.
- mContext = mContext.createConfigurationContext(taskConfig);
- mButtonWindowManager.setConfiguration(taskConfig);
- if (mHintWindowManager != null) {
- mHintWindowManager.setConfiguration(taskConfig);
- }
-
- if (mButton == null || prevTaskListener != taskListener) {
- // TaskListener changed, recreate the button for new surface parent.
- release();
- createSizeCompatButton(show);
- return;
- }
-
- if (!taskConfig.windowConfiguration.getBounds()
- .equals(prevTaskConfig.windowConfiguration.getBounds())) {
- // Reposition the UI surfaces.
- updateAllSurfacePositions();
- }
-
- if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
- // Update layout for RTL.
- mButton.setLayoutDirection(taskConfig.getLayoutDirection());
- updateButtonSurfacePosition();
- if (mHint != null) {
- mHint.setLayoutDirection(taskConfig.getLayoutDirection());
- updateHintSurfacePosition();
- }
- }
- }
-
- /** Called when display layout changed. */
- void updateDisplayLayout(DisplayLayout displayLayout) {
- final Rect prevStableBounds = mStableBounds;
- final Rect curStableBounds = new Rect();
- displayLayout.getStableBounds(curStableBounds);
- mDisplayLayout = displayLayout;
- if (!prevStableBounds.equals(curStableBounds)) {
- // Stable bounds changed, update UI surface positions.
- updateAllSurfacePositions();
- mStableBounds.set(curStableBounds);
- }
- }
-
- /** Called when the visibility of the UI should change. */
- void updateVisibility(boolean show) {
- if (mButton == null) {
- // Button may not have been created because it was hidden previously.
- createSizeCompatButton(show);
- return;
- }
-
- // Hide size compat UIs when IME is showing.
- final int newVisibility = show ? View.VISIBLE : View.GONE;
- if (mButton.getVisibility() != newVisibility) {
- mButton.setVisibility(newVisibility);
- }
- if (mHint != null && mHint.getVisibility() != newVisibility) {
- mHint.setVisibility(newVisibility);
- }
- }
-
- /** Gets the layout params for restart button. */
- WindowManager.LayoutParams getButtonWindowLayoutParams() {
- final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
- // Cannot be wrap_content as this determines the actual window size
- mButtonWidth, mButtonHeight,
- TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
- PixelFormat.TRANSLUCENT);
- winParams.token = new Binder();
- winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId());
- winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- return winParams;
- }
-
- /** Gets the layout params for hint popup. */
- WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) {
- final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
- // Cannot be wrap_content as this determines the actual window size
- hint.getMeasuredWidth(), hint.getMeasuredHeight(),
- TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
- PixelFormat.TRANSLUCENT);
- winParams.token = new Binder();
- winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId());
- winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- winParams.windowAnimations = android.R.style.Animation_InputMethod;
- return winParams;
- }
-
- /** Called when it is ready to be placed size compat UI surface. */
- void attachToParentSurface(SurfaceControl.Builder b) {
- mTaskListener.attachChildSurfaceToTask(mTaskId, b);
- }
-
- /** Called when the restart button is clicked. */
- void onRestartButtonClicked() {
- mCallback.onSizeCompatRestartButtonClicked(mTaskId);
- }
-
- /** Called when the restart button is long clicked. */
- void onRestartButtonLongClicked() {
- createSizeCompatHint();
- }
-
- private void updateAllSurfacePositions() {
- updateButtonSurfacePosition();
- updateHintSurfacePosition();
- }
-
- @VisibleForTesting
- void updateButtonSurfacePosition() {
- if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
- return;
- }
- final SurfaceControl leash = mButtonWindowManager.getSurfaceControl();
-
- // Use stable bounds to prevent the button from overlapping with system bars.
- final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
- final Rect stableBounds = new Rect();
- mDisplayLayout.getStableBounds(stableBounds);
- stableBounds.intersect(taskBounds);
-
- // Position of the button in the container coordinate.
- final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? stableBounds.left - taskBounds.left
- : stableBounds.right - taskBounds.left - mButtonWidth;
- final int positionY = stableBounds.bottom - taskBounds.top - mButtonHeight;
-
- updateSurfacePosition(leash, positionX, positionY);
- }
-
- @VisibleForTesting
- void updateHintSurfacePosition() {
- if (mHint == null || mHintWindowManager == null
- || mHintWindowManager.getSurfaceControl() == null) {
- return;
- }
- final SurfaceControl leash = mHintWindowManager.getSurfaceControl();
-
- // Use stable bounds to prevent the hint from overlapping with system bars.
- final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
- final Rect stableBounds = new Rect();
- mDisplayLayout.getStableBounds(stableBounds);
- stableBounds.intersect(taskBounds);
-
- // Position of the hint in the container coordinate.
- final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? stableBounds.left - taskBounds.left + mPopupOffsetX
- : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth();
- final int positionY =
- stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight();
-
- updateSurfacePosition(leash, positionX, positionY);
- }
-
- private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) {
- mSyncQueue.runInSync(t -> {
- if (!leash.isValid()) {
- Log.w(TAG, "The leash has been released.");
- return;
- }
- t.setPosition(leash, positionX, positionY);
- // The size compat UI should be the topmost child of the Task in case there can be more
- // than one children.
- t.setLayer(leash, Integer.MAX_VALUE);
- });
- }
-
- int getDisplayId() {
- return mDisplayId;
- }
-
- int getTaskId() {
- return mTaskId;
- }
-
- private int getLayoutDirection() {
- return mContext.getResources().getConfiguration().getLayoutDirection();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
deleted file mode 100644
index 82f69c3..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.view.IWindow;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
-import android.view.View;
-import android.view.WindowlessWindowManager;
-
-import com.android.wm.shell.R;
-
-/**
- * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or
- * {@link SizeCompatHintPopup}.
- */
-class SizeCompatUIWindowManager extends WindowlessWindowManager {
-
- private Context mContext;
- private final SizeCompatUILayout mLayout;
-
- @Nullable
- private SurfaceControlViewHost mViewHost;
- @Nullable
- private SurfaceControl mLeash;
-
- SizeCompatUIWindowManager(Context context, Configuration config, SizeCompatUILayout layout) {
- super(config, null /* rootSurface */, null /* hostInputToken */);
- mContext = context;
- mLayout = layout;
- }
-
- @Override
- public void setConfiguration(Configuration configuration) {
- super.setConfiguration(configuration);
- mContext = mContext.createConfigurationContext(configuration);
- }
-
- @Override
- protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
- // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
- .setContainerLayer()
- .setName("SizeCompatUILeash")
- .setHidden(false)
- .setCallsite("SizeCompatUIWindowManager#attachToParentSurface");
- mLayout.attachToParentSurface(builder);
- mLeash = builder.build();
- b.setParent(mLeash);
- }
-
- /** Inflates {@link SizeCompatRestartButton} on to the root surface. */
- SizeCompatRestartButton createSizeCompatButton() {
- if (mViewHost != null) {
- throw new IllegalStateException(
- "A UI has already been created with this window manager.");
- }
-
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
- final SizeCompatRestartButton button = (SizeCompatRestartButton)
- LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
- button.inject(mLayout);
- mViewHost.setView(button, mLayout.getButtonWindowLayoutParams());
- return button;
- }
-
- /** Inflates {@link SizeCompatHintPopup} on to the root surface. */
- SizeCompatHintPopup createSizeCompatHint() {
- if (mViewHost != null) {
- throw new IllegalStateException(
- "A UI has already been created with this window manager.");
- }
-
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
- final SizeCompatHintPopup hint = (SizeCompatHintPopup)
- LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
- // Measure how big the hint is.
- hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- hint.inject(mLayout);
- mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint));
- return hint;
- }
-
- /** Releases the surface control and tears down the view hierarchy. */
- void release() {
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
-
- if (mLeash != null) {
- final SurfaceControl leash = mLeash;
- mLayout.mSyncQueue.runInSync(t -> t.remove(leash));
- mLeash = null;
- }
- }
-
- /**
- * Gets {@link SurfaceControl} of the surface holding size compat UI view. @return {@code null}
- * if not feasible.
- */
- @Nullable
- SurfaceControl getSurfaceControl() {
- return mLeash;
- }
-}
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 3d3a630..3cfa541 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
@@ -65,32 +65,33 @@
/**
* Starts a task in a stage.
*/
- oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7;
+ oneway void startTask(int taskId, int position, in Bundle options) = 7;
/**
* Starts a shortcut in a stage.
*/
- oneway void startShortcut(String packageName, String shortcutId, int stage, int position,
+ oneway void startShortcut(String packageName, String shortcutId, int position,
in Bundle options, in UserHandle user) = 8;
/**
* Starts an activity in a stage.
*/
- oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage,
- int position, in Bundle options) = 9;
+ oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int position,
+ in Bundle options) = 9;
/**
* Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
- in Bundle sideOptions, int sidePosition, in RemoteTransition remoteTransition) = 10;
+ in Bundle sideOptions, int sidePosition, float splitRatio,
+ in RemoteTransition remoteTransition) = 10;
/**
* Version of startTasks using legacy transition system.
*/
oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
int sideTaskId, in Bundle sideOptions, int sidePosition,
- in RemoteAnimationAdapter adapter) = 11;
+ float splitRatio, in RemoteAnimationAdapter adapter) = 11;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS
new file mode 100644
index 0000000..7237d2b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-modules splitscreen owner
+chenghsiuchang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index f8c0304..122fc9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -45,15 +44,6 @@
stageTaskUnfoldController);
}
- void moveToTop(Rect rootBounds, WindowContainerTransaction wct) {
- final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */);
- }
-
- void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
- wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
- }
-
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
// No matter if the root task is empty or not, moving the root to bottom because it no
// longer preserves visible child task.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index e86462f..a91dfe1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import java.util.concurrent.Executor;
@@ -87,6 +87,12 @@
*/
void onKeyguardVisibilityChanged(boolean showing);
+ /** Called when device waking up finished. */
+ void onFinishedWakingUp();
+
+ /** Called when device going to sleep finished. */
+ void onFinishedGoingToSleep();
+
/** Get a string representation of a stage type */
static String stageTypeToString(@StageType int stage) {
switch (stage) {
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 8af72a8..8b87df4 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
@@ -22,8 +22,10 @@
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+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 static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -64,9 +66,10 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -99,6 +102,7 @@
static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
static final int EXIT_REASON_SCREEN_LOCKED = 7;
static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
+ static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
@IntDef(value = {
EXIT_REASON_UNKNOWN,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -109,6 +113,7 @@
EXIT_REASON_ROOT_TASK_VANISHED,
EXIT_REASON_SCREEN_LOCKED,
EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP,
+ EXIT_REASON_CHILD_TASK_ENTER_PIP,
})
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
@@ -181,31 +186,36 @@
return mStageCoordinator.isSplitScreenVisible();
}
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
+ if (isSplitScreenVisible()) {
+ int taskId = mStageCoordinator.getTaskId(splitPosition);
+ return mTaskOrganizer.getRunningTaskInfo(taskId);
+ }
+ return null;
+ }
+
+ public boolean isTaskInSplitScreen(int taskId) {
+ return isSplitScreenVisible()
+ && mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
+ }
+
+ public @SplitPosition int getSplitPosition(int taskId) {
+ return mStageCoordinator.getSplitPosition(taskId);
+ }
+
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
+ return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
+ new WindowContainerTransaction());
+ }
+
+ private boolean moveToStage(int taskId, @StageType int stageType,
+ @SplitPosition int stagePosition, WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
}
- return moveToSideStage(task, sideStagePosition);
- }
-
- public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
- WindowContainerTransaction wct) {
- final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
- if (task == null) {
- throw new IllegalArgumentException("Unknown taskId" + taskId);
- }
- return moveToSideStage(task, sideStagePosition, wct);
- }
-
- public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitPosition int sideStagePosition) {
- return mStageCoordinator.moveToSideStage(task, sideStagePosition);
- }
-
- public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
- return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
+ return mStageCoordinator.moveToStage(task, stageType, stagePosition, wct);
}
public boolean removeFromSideStage(int taskId) {
@@ -221,13 +231,14 @@
}
public void enterSplitScreen(int taskId, boolean leftOrTop) {
- moveToSideStage(taskId,
- leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
}
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
- moveToSideStage(taskId,
- leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
+ final int stagePosition =
+ leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ moveToStage(taskId, stageType, stagePosition, wct);
}
public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
@@ -242,6 +253,14 @@
mStageCoordinator.onKeyguardVisibilityChanged(showing);
}
+ public void onFinishedWakingUp() {
+ mStageCoordinator.onFinishedWakingUp();
+ }
+
+ public void onFinishedGoingToSleep() {
+ mStageCoordinator.onFinishedGoingToSleep();
+ }
+
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
}
@@ -258,9 +277,9 @@
mStageCoordinator.unregisterSplitScreenListener(listener);
}
- public void startTask(int taskId, @SplitScreen.StageType int stage,
- @SplitPosition int position, @Nullable Bundle options) {
- options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+ public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
try {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
@@ -275,10 +294,10 @@
}
}
- public void startShortcut(String packageName, String shortcutId,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) {
- options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
@@ -293,20 +312,18 @@
}
}
- public void startIntent(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
- startIntentLegacy(intent, fillInIntent, stage, position, options);
+ startIntentLegacy(intent, fillInIntent, position, options);
return;
}
- mStageCoordinator.startIntent(intent, fillInIntent, stage, position, options,
+ mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options,
null /* remote */);
}
private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
- @Nullable Bundle options) {
+ @SplitPosition int position, @Nullable Bundle options) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
@@ -340,13 +357,13 @@
};
final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = mStageCoordinator.resolveStartStage(stage, position, options, wct);
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
- if (!isSplitScreenVisible()) return null;
+ if (apps.length < 2) return null;
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
.setContainerLayer()
.setName("RecentsAnimationSplitTasks")
@@ -400,6 +417,8 @@
return "APP_FINISHED";
case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
+ case EXIT_REASON_CHILD_TASK_ENTER_PIP:
+ return "CHILD_TASK_ENTER_PIP";
default:
return "unknown reason, reason int = " + exitReason;
}
@@ -501,6 +520,20 @@
SplitScreenController.this.onKeyguardVisibilityChanged(showing);
});
}
+
+ @Override
+ public void onFinishedWakingUp() {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.onFinishedWakingUp();
+ });
+ }
+
+ @Override
+ public void onFinishedGoingToSleep() {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.onFinishedGoingToSleep();
+ });
+ }
}
/**
@@ -583,49 +616,48 @@
}
@Override
- public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
+ public void startTask(int taskId, int position, @Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startTask",
(controller) -> {
- controller.startTask(taskId, stage, position, options);
+ controller.startTask(taskId, position, options);
});
}
@Override
public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
- adapter));
+ splitRatio, adapter));
}
@Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition,
+ @SplitPosition int sidePosition, float splitRatio,
@Nullable RemoteTransition remoteTransition) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
- sideTaskId, sideOptions, sidePosition, remoteTransition));
+ sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition));
}
@Override
- public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ public void startShortcut(String packageName, String shortcutId, int position,
@Nullable Bundle options, UserHandle user) {
executeRemoteCallWithTaskPermission(mController, "startShortcut",
(controller) -> {
- controller.startShortcut(packageName, shortcutId, stage, position,
- options, user);
+ controller.startShortcut(packageName, shortcutId, position, options, user);
});
}
@Override
- public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
+ public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
@Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startIntent",
(controller) -> {
- controller.startIntent(intent, fillInIntent, stage, position, options);
+ controller.startIntent(intent, fillInIntent, position, options);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index e320c2a..3e7a100 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -26,8 +26,8 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
@@ -43,7 +43,7 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
/**
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 d494191..5d1d159 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
@@ -26,15 +26,16 @@
import static android.view.WindowManager.transitTypeToString;
import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+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 static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -89,10 +90,11 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.StagedSplitBounds;
@@ -155,12 +157,14 @@
private boolean mShouldUpdateRecents;
private boolean mExitSplitScreenOnHide;
private boolean mKeyguardOccluded;
+ private boolean mDeviceSleep;
+ private boolean mIsDividerRemoteAnimating;
- @SplitScreen.StageType
+ @StageType
private int mDismissTop = NO_DISMISS;
/** The target stage to dismiss to when unlock after folded. */
- @SplitScreen.StageType
+ @StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
private final Runnable mOnTransitionAnimationComplete = () -> {
@@ -273,18 +277,42 @@
return mSideStageListener.mVisible && mMainStageListener.mVisible;
}
- boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitPosition int sideStagePosition) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- return moveToSideStage(task, sideStagePosition, wct);
+ @StageType
+ int getStageOfTask(int taskId) {
+ if (mMainStage.containsTask(taskId)) {
+ return STAGE_TYPE_MAIN;
+ } else if (mSideStage.containsTask(taskId)) {
+ return STAGE_TYPE_SIDE;
+ }
+
+ return STAGE_TYPE_UNDEFINED;
}
- boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ boolean moveToStage(ActivityManager.RunningTaskInfo task, @StageType int stageType,
+ @SplitPosition int stagePosition, WindowContainerTransaction wct) {
+ StageTaskListener targetStage;
+ int sideStagePosition;
+ if (stageType == STAGE_TYPE_MAIN) {
+ targetStage = mMainStage;
+ sideStagePosition = SplitLayout.reversePosition(stagePosition);
+ } else if (stageType == STAGE_TYPE_SIDE) {
+ targetStage = mSideStage;
+ sideStagePosition = stagePosition;
+ } else {
+ if (mMainStage.isActive()) {
+ // If the split screen is activated, retrieves target stage based on position.
+ targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage;
+ sideStagePosition = mSideStagePosition;
+ } else {
+ targetStage = mSideStage;
+ sideStagePosition = stagePosition;
+ }
+ }
+
setSideStagePosition(sideStagePosition, wct);
- mSideStage.evictAllChildren(evictWct);
- mSideStage.addTask(task, wct);
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ targetStage.evictAllChildren(evictWct);
+ targetStage.addTask(task, wct);
if (!evictWct.isEmpty()) {
wct.merge(evictWct, true /* transfer */);
}
@@ -308,7 +336,7 @@
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
- @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@Nullable RemoteTransition remoteTransition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -320,6 +348,7 @@
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
addActivityOptions(sideOptions, mSideStage);
@@ -335,8 +364,15 @@
/** Starts 2 tasks in one legacy transition. */
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter) {
+ // Init divider first to make divider leash for remote animation target.
+ setDividerVisibility(true /* visible */);
+ // Set false to avoid record new bounds with old task still on top;
+ mShouldUpdateRecents = false;
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() {
@@ -346,12 +382,25 @@
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
+ mIsDividerRemoteAnimating = true;
RemoteAnimationTarget[] augmentedNonApps =
new RemoteAnimationTarget[nonApps.length + 1];
for (int i = 0; i < nonApps.length; ++i) {
augmentedNonApps[i] = nonApps[i];
}
augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+
+ IRemoteAnimationFinishedCallback wrapCallback =
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ mIsDividerRemoteAnimating = false;
+ mShouldUpdateRecents = true;
+ mSyncQueue.queue(evictWct);
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ finishedCallback.onAnimationFinished();
+ }
+ };
try {
try {
ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
@@ -360,8 +409,8 @@
Slog.e(TAG, "Unable to boost animation thread. This should only happen"
+ " during unit tests");
}
- adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
- finishedCallback);
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
+ augmentedNonApps, wrapCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e);
}
@@ -369,6 +418,10 @@
@Override
public void onAnimationCancelled() {
+ mIsDividerRemoteAnimating = false;
+ mShouldUpdateRecents = true;
+ mSyncQueue.queue(evictWct);
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
try {
adapter.getRunner().onAnimationCancelled();
} catch (RemoteException e) {
@@ -390,10 +443,15 @@
sideOptions = sideOptions != null ? sideOptions : new Bundle();
setSideStagePosition(sidePosition, wct);
- // 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(getMainStageBounds(), wct, false /* reparent */);
- mSideStage.setBounds(getSideStageBounds(), wct);
+ mSplitLayout.setDivideRatio(splitRatio);
+ if (mMainStage.isActive()) {
+ mMainStage.moveToTop(getMainStageBounds(), wct);
+ } else {
+ // 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(getMainStageBounds(), wct, false /* reparent */);
+ }
+ mSideStage.moveToTop(getSideStageBounds(), wct);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
@@ -408,7 +466,7 @@
}
public void startIntent(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ @StageType int stage, @SplitPosition int position,
@androidx.annotation.Nullable Bundle options,
@Nullable RemoteTransition remoteTransition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -430,17 +488,20 @@
}
}
- Bundle resolveStartStage(@SplitScreen.StageType int stage,
+ Bundle resolveStartStage(@StageType int stage,
@SplitPosition int position, @androidx.annotation.Nullable Bundle options,
@androidx.annotation.Nullable WindowContainerTransaction wct) {
switch (stage) {
case STAGE_TYPE_UNDEFINED: {
- // Use the stage of the specified position is valid.
if (position != SPLIT_POSITION_UNDEFINED) {
- if (position == getSideStagePosition()) {
- options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
+ if (mMainStage.isActive()) {
+ // Use the stage of the specified position
+ options = resolveStartStage(
+ position == mSideStagePosition ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN,
+ position, options, wct);
} else {
- options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct);
+ // Use the side stage as default to active split screen
+ options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
}
} else {
// Exit split-screen and launch fullscreen since stage wasn't specified.
@@ -463,9 +524,7 @@
case STAGE_TYPE_MAIN: {
if (position != SPLIT_POSITION_UNDEFINED) {
// Set the side stage opposite of what we want to the main stage.
- final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
- setSideStagePosition(sideStagePosition, wct);
+ setSideStagePosition(SplitLayout.reversePosition(position), wct);
} else {
position = getMainStagePosition();
}
@@ -482,15 +541,22 @@
return options;
}
- @SplitLayout.SplitPosition
+ @SplitPosition
int getSideStagePosition() {
return mSideStagePosition;
}
- @SplitLayout.SplitPosition
+ @SplitPosition
int getMainStagePosition() {
- return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
+ return SplitLayout.reversePosition(mSideStagePosition);
+ }
+
+ int getTaskId(@SplitPosition int splitPosition) {
+ if (mSideStagePosition == splitPosition) {
+ return mSideStage.getTopVisibleChildTaskId();
+ } else {
+ return mMainStage.getTopVisibleChildTaskId();
+ }
}
void setSideStagePosition(@SplitPosition int sideStagePosition,
@@ -537,6 +603,17 @@
}
}
+ void onFinishedWakingUp() {
+ if (mMainStage.isActive()) {
+ exitSplitScreenIfKeyguardOccluded();
+ }
+ mDeviceSleep = false;
+ }
+
+ void onFinishedGoingToSleep() {
+ mDeviceSleep = true;
+ }
+
void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mExitSplitScreenOnHide = exitSplitScreenOnHide;
}
@@ -565,6 +642,19 @@
applyExitSplitScreen(childrenToTop, wct, exitReason);
}
+ private void exitSplitScreenIfKeyguardOccluded() {
+ final boolean mainStageVisible = mMainStageListener.mVisible;
+ final boolean oneStageVisible = mainStageVisible ^ mSideStageListener.mVisible;
+ if (mDeviceSleep && mKeyguardOccluded && oneStageVisible) {
+ // Only the stages include show-when-locked activity is visible while keyguard occluded.
+ // Dismiss split because there's show-when-locked activity showing on top of keyguard.
+ // Also make sure the task contains show-when-locked activity remains on top after split
+ // dismissed.
+ final StageTaskListener toTop = mainStageVisible ? mMainStage : mSideStage;
+ exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
+ }
+ }
+
private void applyExitSplitScreen(StageTaskListener childrenToTop,
WindowContainerTransaction wct, @ExitReason int exitReason) {
mRecentTasks.ifPresent(recentTasks -> {
@@ -577,8 +667,12 @@
});
mShouldUpdateRecents = false;
- mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
- mMainStage.deactivate(wct, childrenToTop == mMainStage);
+ // When the exit split-screen is caused by one of the task enters auto pip,
+ // we want the tasks to be put to bottom instead of top, otherwise it will end up
+ // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
+ final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP;
+ mSideStage.removeAllTasks(wct, !fromEnteringPip && childrenToTop == mSideStage);
+ mMainStage.deactivate(wct, !fromEnteringPip && childrenToTop == mMainStage);
mTaskOrganizer.applyTransaction(wct);
mSyncQueue.runInSync(t -> t
.setWindowCrop(mMainStage.mRootLeash, null)
@@ -608,6 +702,8 @@
case EXIT_REASON_DRAG_DIVIDER:
// Either of the split apps have finished
case EXIT_REASON_APP_FINISHED:
+ // One of the children enters PiP
+ case EXIT_REASON_CHILD_TASK_ENTER_PIP:
return true;
default:
return false;
@@ -619,7 +715,7 @@
* an existing WindowContainerTransaction (rather than applying immediately). This is intended
* to be used when exiting split might be bundled with other window operations.
*/
- void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop,
+ void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
@@ -630,6 +726,16 @@
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
}
+ @SplitPosition
+ int getSplitPosition(int taskId) {
+ if (mSideStage.getTopVisibleChildTaskId() == taskId) {
+ return getSideStagePosition();
+ } else if (mMainStage.getTopVisibleChildTaskId() == taskId) {
+ return getMainStagePosition();
+ }
+ return SPLIT_POSITION_UNDEFINED;
+ }
+
private void addActivityOptions(Bundle opts, StageTaskListener stage) {
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
}
@@ -680,18 +786,24 @@
mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
}
- updateRecentTasksSplitPair();
+ if (present && visible) {
+ updateRecentTasksSplitPair();
+ }
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
}
}
+ private void onStageChildTaskEnterPip(StageListenerImpl stageListener, int taskId) {
+ exitSplitScreen(stageListener == mMainStageListener ? mMainStage : mSideStage,
+ EXIT_REASON_CHILD_TASK_ENTER_PIP);
+ }
+
private void updateRecentTasksSplitPair() {
if (!mShouldUpdateRecents) {
return;
}
-
mRecentTasks.ifPresent(recentTasks -> {
Rect topLeftBounds = mSplitLayout.getBounds1();
Rect bottomRightBounds = mSplitLayout.getBounds2();
@@ -732,7 +844,9 @@
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make the stages adjacent to each other so they occlude what's behind them.
- wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
+ true /* moveTogether */);
+ wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
mTaskOrganizer.applyTransaction(wct);
}
}
@@ -740,6 +854,7 @@
private void onStageRootTaskVanished(StageListenerImpl stageListener) {
if (stageListener == mMainStageListener || stageListener == mSideStageListener) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
// Deactivate the main stage if it no longer has a root task.
mMainStage.deactivate(wct);
mTaskOrganizer.applyTransaction(wct);
@@ -747,7 +862,7 @@
}
private void setDividerVisibility(boolean visible) {
- if (mDividerVisible == visible) return;
+ if (mIsDividerRemoteAnimating || mDividerVisible == visible) return;
mDividerVisible = visible;
if (visible) {
mSplitLayout.init();
@@ -780,14 +895,8 @@
// like the cases keyguard showing or screen off.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
}
- } else if (mKeyguardOccluded) {
- // At least one of the stages is visible while keyguard occluded. Dismiss split because
- // there's show-when-locked activity showing on top of keyguard. Also make sure the
- // task contains show-when-locked activity remains on top after split dismissed.
- final StageTaskListener toTop =
- mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null);
- exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
}
+ exitSplitScreenIfKeyguardOccluded();
mSyncQueue.runInSync(t -> {
// Same above, we only set root tasks and divider leash visibility when both stage
@@ -801,13 +910,14 @@
}
private void applyDividerVisibility(SurfaceControl.Transaction t) {
+ if (mIsDividerRemoteAnimating) return;
+
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) {
- return;
- }
+ if (dividerLeash == null) return;
if (mDividerVisible) {
t.show(dividerLeash)
+ .setAlpha(dividerLeash, 1)
.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER)
.setPosition(dividerLeash,
mSplitLayout.getDividerBounds().left,
@@ -870,8 +980,7 @@
@Override
public void onDoubleTappedDivider() {
- setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+ setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), null /* wct */);
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
@@ -1029,7 +1138,7 @@
return null;
}
- @SplitScreen.StageType
+ @StageType
private int getStageType(StageTaskListener stage) {
return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
}
@@ -1156,7 +1265,7 @@
final TransitionInfo.Change change = info.getChanges().get(iC);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo == null || !taskInfo.hasParentTask()) continue;
- final @SplitScreen.StageType int stageType = getStageType(getStageOfTask(taskInfo));
+ final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
if (stageType == STAGE_TYPE_MAIN) {
mainChild = change;
} else if (stageType == STAGE_TYPE_SIDE) {
@@ -1296,11 +1405,16 @@
pw.println(prefix + TAG + " mDisplayId=" + mDisplayId);
pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible);
pw.println(innerPrefix + "MainStage");
+ pw.println(childPrefix + "stagePosition=" + getMainStagePosition());
pw.println(childPrefix + "isActive=" + mMainStage.isActive());
mMainStageListener.dump(pw, childPrefix);
pw.println(innerPrefix + "SideStage");
+ pw.println(childPrefix + "stagePosition=" + getSideStagePosition());
mSideStageListener.dump(pw, childPrefix);
- pw.println(innerPrefix + "mSplitLayout=" + mSplitLayout);
+ if (mMainStage.isActive()) {
+ pw.println(innerPrefix + "SplitLayout");
+ mSplitLayout.dump(pw, childPrefix);
+ }
}
/**
@@ -1374,6 +1488,11 @@
}
@Override
+ public void onChildTaskEnterPip(int taskId) {
+ StageCoordinator.this.onStageChildTaskEnterPip(this, taskId);
+ }
+
+ @Override
public void onRootTaskVanished() {
reset();
StageCoordinator.this.onStageRootTaskVanished(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 62b8638..04e20db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -33,6 +34,7 @@
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -43,6 +45,7 @@
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitDecorManager;
+import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import java.io.PrintWriter;
@@ -72,6 +75,8 @@
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
+ void onChildTaskEnterPip(int taskId);
+
void onRootTaskVanished();
void onNoLongerSupportMultiWindow();
@@ -255,6 +260,9 @@
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ mCallbacks.onChildTaskEnterPip(taskId);
+ }
if (ENABLE_SHELL_TRANSITIONS) {
// Status is managed/synchronized by the transition lifecycle.
return;
@@ -293,6 +301,15 @@
}
}
+ void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
+ wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
+ }
+
+ void moveToTop(Rect rootBounds, WindowContainerTransaction wct) {
+ final WindowContainerToken rootToken = mRootTaskInfo.token;
+ wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */);
+ }
+
void setBounds(Rect bounds, WindowContainerTransaction wct) {
wct.setBounds(mRootTaskInfo.token, bounds);
}
@@ -317,7 +334,7 @@
}
void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
- @SplitScreen.StageType int stage) {
+ @StageType int stage) {
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
int taskId = mChildrenTaskInfo.keyAt(i);
listener.onTaskStageChanged(taskId, stage,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
index e904f6a..4849163 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
@@ -100,6 +100,9 @@
* @param leash surface leash for the appeared task
*/
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ // Only handle child task surface here.
+ if (!taskInfo.hasParentTask()) return;
+
AnimationContext context = new AnimationContext(leash);
mAnimationContextByTaskId.put(taskInfo.taskId, context);
}
@@ -109,6 +112,8 @@
* @param taskInfo info for the vanished task
*/
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (!taskInfo.hasParentTask()) return;
+
AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
if (context != null) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS
new file mode 100644
index 0000000..264e88f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-modules stagesplit owner
+chenghsiuchang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java
index aec81a1..c5d2312 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import java.util.concurrent.Executor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
index 94db9cd9..f1520ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
@@ -20,8 +20,9 @@
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+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 static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -61,7 +62,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -208,9 +209,9 @@
mStageCoordinator.unregisterSplitScreenListener(listener);
}
- public void startTask(int taskId, @SplitScreen.StageType int stage,
- @SplitPosition int position, @Nullable Bundle options) {
- options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+ public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
try {
ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
@@ -219,10 +220,10 @@
}
}
- public void startShortcut(String packageName, String shortcutId,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) {
- options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
try {
LauncherApps launcherApps =
@@ -234,20 +235,18 @@
}
}
- public void startIntent(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
- startIntentLegacy(intent, fillInIntent, stage, position, options);
+ startIntentLegacy(intent, fillInIntent, position, options);
return;
}
- mStageCoordinator.startIntent(intent, fillInIntent, stage, position, options,
+ mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options,
null /* remote */);
}
private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
- @Nullable Bundle options) {
+ @SplitPosition int position, @Nullable Bundle options) {
LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
@Override
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
@@ -275,7 +274,7 @@
}
};
WindowContainerTransaction wct = new WindowContainerTransaction();
- options = mStageCoordinator.resolveStartStage(stage, position, options, wct);
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
@@ -539,7 +538,7 @@
public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startTask",
(controller) -> {
- controller.startTask(taskId, stage, position, options);
+ controller.startTask(taskId, position, options);
});
}
@@ -568,7 +567,7 @@
@Nullable Bundle options, UserHandle user) {
executeRemoteCallWithTaskPermission(mController, "startShortcut",
(controller) -> {
- controller.startShortcut(packageName, shortcutId, stage, position,
+ controller.startShortcut(packageName, shortcutId, position,
options, user);
});
}
@@ -578,7 +577,7 @@
@Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startIntent",
(controller) -> {
- controller.startIntent(intent, fillInIntent, stage, position, options);
+ controller.startIntent(intent, fillInIntent, position, options);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java
index aab7902..e185039 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java
@@ -17,13 +17,13 @@
package com.android.wm.shell.stagesplit;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
/**
* Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
index 60a6cd7..a17942f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
@@ -31,9 +31,9 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+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 static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -87,7 +87,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
@@ -634,7 +634,8 @@
mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit);
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make the stages adjacent to each other so they occlude what's behind them.
- wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
+ true /* moveTogether */);
// Only sets side stage as launch-adjacent-flag-root when the device is not using legacy
// split to prevent new split behavior confusing users.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 003d8a3..e7b5744 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -72,6 +72,7 @@
private final int mAppRevealDuration;
private final int mAnimationDuration;
private final float mIconStartAlpha;
+ private final float mBrandingStartAlpha;
private final TransactionPool mTransactionPool;
private ValueAnimator mMainAnimator;
@@ -94,9 +95,17 @@
|| iconView.getLayoutParams().height == 0) {
mIconFadeOutDuration = 0;
mIconStartAlpha = 0;
+ mBrandingStartAlpha = 0;
mAppRevealDelay = 0;
} else {
iconView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ // The branding view could only exists when the icon is present.
+ final View brandingView = view.getBrandingView();
+ if (brandingView != null) {
+ mBrandingStartAlpha = brandingView.getAlpha();
+ } else {
+ mBrandingStartAlpha = 0;
+ }
mIconFadeOutDuration = context.getResources().getInteger(
R.integer.starting_window_app_reveal_icon_fade_out_duration);
mAppRevealDelay = context.getResources().getInteger(
@@ -334,13 +343,21 @@
// ignore
}
- private void onAnimationProgress(float linearProgress) {
- View iconView = mSplashScreenView.getIconView();
+ private void onFadeOutProgress(float linearProgress) {
+ final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
+ getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
+ final View iconView = mSplashScreenView.getIconView();
+ final View brandingView = mSplashScreenView.getBrandingView();
if (iconView != null) {
- final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
- getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
iconView.setAlpha(mIconStartAlpha * (1 - iconProgress));
}
+ if (brandingView != null) {
+ brandingView.setAlpha(mBrandingStartAlpha * (1 - iconProgress));
+ }
+ }
+
+ private void onAnimationProgress(float linearProgress) {
+ onFadeOutProgress(linearProgress);
final float revealLinearProgress = getProgress(linearProgress, mAppRevealDelay,
mAppRevealDuration);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index bd48696..270107c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -134,7 +134,8 @@
mDisplayManager.getDisplay(DEFAULT_DISPLAY);
}
- private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
+ @VisibleForTesting
+ final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
/**
* Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
@@ -464,8 +465,24 @@
Slog.d(TAG, "Task start finish, remove starting surface for task "
+ removalInfo.taskId);
}
- removeWindowSynced(removalInfo);
+ removeWindowSynced(removalInfo, false /* immediately */);
+ }
+ /**
+ * Clear all starting windows immediately.
+ */
+ public void clearAllWindows() {
+ if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
+ Slog.d(TAG, "Clear all starting windows immediately");
+ }
+ final int taskSize = mStartingWindowRecords.size();
+ final int[] taskIds = new int[taskSize];
+ for (int i = taskSize - 1; i >= 0; --i) {
+ taskIds[i] = mStartingWindowRecords.keyAt(i);
+ }
+ for (int i = taskSize - 1; i >= 0; --i) {
+ removeWindowNoAnimate(taskIds[i]);
+ }
}
/**
@@ -550,7 +567,8 @@
return shouldSaveView;
}
- private void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
+ @VisibleForTesting
+ void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
@StartingWindowType int suggestType) {
final StartingWindowRecord tView = new StartingWindowRecord(appToken, view,
null/* TaskSnapshotWindow */, suggestType);
@@ -559,19 +577,18 @@
private void removeWindowNoAnimate(int taskId) {
mTmpRemovalInfo.taskId = taskId;
- removeWindowSynced(mTmpRemovalInfo);
+ removeWindowSynced(mTmpRemovalInfo, true /* immediately */);
}
void onImeDrawnOnTask(int taskId) {
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null && record.mTaskSnapshotWindow != null
&& record.mTaskSnapshotWindow.hasImeSurface()) {
- record.mTaskSnapshotWindow.removeImmediately();
+ removeWindowNoAnimate(taskId);
}
- mStartingWindowRecords.remove(taskId);
}
- protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo) {
+ protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, boolean immediately) {
final int taskId = removalInfo.taskId;
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null) {
@@ -580,7 +597,8 @@
Slog.v(TAG, "Removing splash screen window for task: " + taskId);
}
if (record.mContentView != null) {
- if (record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
+ if (immediately
+ || record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
removeWindowInner(record.mDecorView, false);
} else {
if (removalInfo.playRevealAnimation) {
@@ -604,8 +622,12 @@
if (DEBUG_TASK_SNAPSHOT) {
Slog.v(TAG, "Removing task snapshot window for " + taskId);
}
- record.mTaskSnapshotWindow.scheduleRemove(
- () -> mStartingWindowRecords.remove(taskId), removalInfo.deferRemoveForIme);
+ if (immediately) {
+ record.mTaskSnapshotWindow.removeImmediately();
+ } else {
+ record.mTaskSnapshotWindow.scheduleRemove(() ->
+ mStartingWindowRecords.remove(taskId), removalInfo.deferRemoveForIme);
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index e98a3e8..b0a6605 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -29,9 +29,7 @@
import android.content.Context;
import android.graphics.Color;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.Trace;
-import android.util.Slog;
import android.util.SparseIntArray;
import android.window.StartingWindowInfo;
import android.window.StartingWindowInfo.StartingWindowType;
@@ -199,6 +197,18 @@
}
/**
+ * Clear all starting window immediately, called this method when releasing the task organizer.
+ */
+ public void clearAllWindows() {
+ mSplashScreenExecutor.execute(() -> {
+ mStartingSurfaceDrawer.clearAllWindows();
+ synchronized (mTaskBackgroundColors) {
+ mTaskBackgroundColors.clear();
+ }
+ });
+ }
+
+ /**
* The interface for calls from outside the Shell, within the host process.
*/
private class StartingSurfaceImpl implements StartingSurface {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
index aadf792..a0c84cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Container of various information needed to display split screen
* tasks/leashes/etc in Launcher
@@ -93,6 +95,24 @@
}
@Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof StagedSplitBounds)) {
+ return false;
+ }
+ // Only need to check the base fields (the other fields are derived from these)
+ final StagedSplitBounds other = (StagedSplitBounds) obj;
+ return Objects.equals(leftTopBounds, other.leftTopBounds)
+ && Objects.equals(rightBottomBounds, other.rightBottomBounds)
+ && leftTopTaskId == other.leftTopTaskId
+ && rightBottomTaskId == other.rightBottomTaskId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(leftTopBounds, rightBottomBounds, leftTopTaskId, rightBottomTaskId);
+ }
+
+ @Override
public String toString() {
return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+ "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n"
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 1fcbf14..825320b 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
@@ -37,6 +37,7 @@
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ParceledListSlice;
@@ -56,7 +57,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
+import com.android.wm.shell.compatui.CompatUIController;
import org.junit.Before;
import org.junit.Test;
@@ -82,7 +83,7 @@
@Mock
private Context mContext;
@Mock
- private SizeCompatUIController mSizeCompatUI;
+ private CompatUIController mCompatUI;
ShellTaskOrganizer mOrganizer;
private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
@@ -132,7 +133,7 @@
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
- mSizeCompatUI, Optional.empty()));
+ mCompatUI, Optional.empty()));
}
@Test
@@ -334,35 +335,102 @@
mOrganizer.onTaskAppeared(taskInfo1, null);
// sizeCompatActivity is null if top activity is not in size compat.
- verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
// sizeCompatActivity is non-null if top activity is in size compat.
- clearInvocations(mSizeCompatUI);
+ clearInvocations(mCompatUI);
final RunningTaskInfo taskInfo2 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo2.displayId = taskInfo1.displayId;
taskInfo2.topActivityInSizeCompat = true;
taskInfo2.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo2);
- verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- taskInfo1.configuration, taskListener);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
// Not show size compat UI if task is not visible.
- clearInvocations(mSizeCompatUI);
+ clearInvocations(mCompatUI);
final RunningTaskInfo taskInfo3 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo3.displayId = taskInfo1.displayId;
taskInfo3.topActivityInSizeCompat = true;
taskInfo3.isVisible = false;
mOrganizer.onTaskInfoChanged(taskInfo3);
- verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */);
- clearInvocations(mSizeCompatUI);
+ clearInvocations(mCompatUI);
mOrganizer.onTaskVanished(taskInfo1);
- verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+ }
+
+ @Test
+ public void testOnCameraCompatActivityChanged() {
+ final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
+ taskInfo1.displayId = DEFAULT_DISPLAY;
+ taskInfo1.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+ final TrackingTaskListener taskListener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+
+ // Task listener sent to compat UI is null if top activity doesn't request a camera
+ // compat control.
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+
+ // Task linster is non-null when request a camera compat control for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo2 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo2.displayId = taskInfo1.displayId;
+ taskInfo2.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ taskInfo2.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
+
+ // CompatUIController#onCompatInfoChanged is called when requested state for a camera
+ // compat control changes for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo3 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo3.displayId = taskInfo1.displayId;
+ taskInfo3.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+ taskInfo3.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo3);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, taskListener);
+
+ // CompatUIController#onCompatInfoChanged is called when a top activity goes in size compat
+ // mode for a visible task that has a compat control.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo4 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo4.displayId = taskInfo1.displayId;
+ taskInfo4.topActivityInSizeCompat = true;
+ taskInfo4.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+ taskInfo4.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo4);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo4, taskListener);
+
+ // Task linster is null when a camera compat control is dimissed for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo5 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo5.displayId = taskInfo1.displayId;
+ taskInfo5.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+ taskInfo5.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo5);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo5, null /* taskListener */);
+
+ // Task linster is null when request a camera compat control for a invisible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo6 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo6.displayId = taskInfo1.displayId;
+ taskInfo6.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ taskInfo6.isVisible = false;
+ mOrganizer.onTaskInfoChanged(taskInfo6);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo6, null /* taskListener */);
+
+ clearInvocations(mCompatUI);
+ mOrganizer.onTaskVanished(taskInfo1);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index bc701d0..8bc1223 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -39,6 +39,7 @@
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Log;
import android.util.Pair;
import android.view.WindowManager;
@@ -913,6 +914,31 @@
assertSelectionChangedTo(mBubbleA2);
}
+ /**
+ * - have a maxed out bubble stack & all of the bubbles have been recently accessed
+ * - bubble a notification that was posted before any of those bubbles were accessed
+ * => that bubble should be added
+ *
+ */
+ @Test
+ public void test_addOldNotifWithNewerBubbles() {
+ sendUpdatedEntryAtTime(mEntryA1, 2000);
+ sendUpdatedEntryAtTime(mEntryA2, 3000);
+ sendUpdatedEntryAtTime(mEntryA3, 4000);
+ sendUpdatedEntryAtTime(mEntryB1, 5000);
+ sendUpdatedEntryAtTime(mEntryB2, 6000);
+
+ mBubbleData.setListener(mListener);
+ sendUpdatedEntryAtTime(mEntryB3, 1000 /* postTime */, 7000 /* currentTime */);
+ verifyUpdateReceived();
+
+ // B3 is in the stack
+ assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleB3.getKey())).isNotNull();
+ // A1 is the oldest so it's in the overflow
+ assertThat(mBubbleData.getOverflowBubbleWithKey(mEntryA1.getKey())).isNotNull();
+ assertOrderChangedTo(mBubbleB3, mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2);
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
@@ -1014,6 +1040,12 @@
}
private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) {
+ setCurrentTime(postTime);
+ sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
+ }
+
+ private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, long currentTime) {
+ setCurrentTime(currentTime);
sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index 2b9bdce..335222e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -59,19 +59,21 @@
private int mStackOffset;
private PointF mExpansionPoint;
private BubblePositioner mPositioner;
- private BubbleStackView.StackViewState mStackViewState;
+ private BubbleStackView.StackViewState mStackViewState = new BubbleStackView.StackViewState();
@SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
super.setUp();
- BubbleStackView stackView = mock(BubbleStackView.class);
- when(stackView.getState()).thenReturn(getStackViewState());
mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, mDisplayWidth, mDisplayHeight));
+
+ BubbleStackView stackView = mock(BubbleStackView.class);
+ when(stackView.getState()).thenReturn(getStackViewState());
+
mExpandedController = new ExpandedAnimationController(mPositioner,
mOnBubbleAnimatedOutAction,
stackView);
@@ -135,6 +137,12 @@
testBubblesInCorrectExpandedPositions();
}
+ @Test
+ public void testDragBubbleOutDoesntNPE() throws InterruptedException {
+ mExpandedController.onGestureFinished();
+ mExpandedController.dragBubbleOut(mViews.get(0), 1, 1);
+ }
+
/** Expand the stack and wait for animations to finish. */
private void expand() throws InterruptedException {
mExpandedController.expandFromStack(mock(Runnable.class));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 73eebad..453050f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -106,6 +106,12 @@
}
@Test
+ public void testSetDivideRatio() {
+ mSplitLayout.setDivideRatio(0.5f);
+ verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
+ }
+
+ @Test
public void testOnDoubleTappedDivider() {
mSplitLayout.onDoubleTappedDivider();
verify(mSplitLayoutHandler).onDoubleTappedDivider();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
new file mode 100644
index 0000000..4352fd3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -0,0 +1,338 @@
+/*
+ * 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.wm.shell.compatui;
+
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.testing.AndroidTestingRunner;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUIController}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:CompatUIControllerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUIControllerTest extends ShellTestCase {
+ private static final int DISPLAY_ID = 0;
+ private static final int TASK_ID = 12;
+
+ private CompatUIController mController;
+ private @Mock DisplayController mMockDisplayController;
+ private @Mock DisplayInsetsController mMockDisplayInsetsController;
+ private @Mock DisplayLayout mMockDisplayLayout;
+ private @Mock DisplayImeController mMockImeController;
+ private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
+ private @Mock SyncTransactionQueue mMockSyncQueue;
+ private @Mock ShellExecutor mMockExecutor;
+ private @Mock CompatUIWindowManager mMockLayout;
+
+ @Captor
+ ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
+ doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
+ doReturn(TASK_ID).when(mMockLayout).getTaskId();
+ mController = new CompatUIController(mContext, mMockDisplayController,
+ mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
+ @Override
+ CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
+ return mMockLayout;
+ }
+ };
+ spyOn(mController);
+ }
+
+ @Test
+ public void testListenerRegistered() {
+ verify(mMockDisplayController).addDisplayWindowListener(mController);
+ verify(mMockImeController).addPositionProcessor(mController);
+ }
+
+ @Test
+ public void testOnCompatInfoChanged() {
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Verify that the restart button is added with non-null size compat info.
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
+
+ verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
+
+ // Verify that the restart button is updated with non-null new size compat info.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Verify that the restart button is updated with new camera state.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Verify that compat controls are removed with null compat info.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
+ null /* taskListener */);
+
+ verify(mMockLayout).release();
+
+ clearInvocations(mMockLayout);
+ clearInvocations(mController);
+ // Verify that compat controls are removed with dismissed camera state.
+ taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
+
+ verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
+
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_DISMISSED),
+ null /* taskListener */);
+
+ verify(mMockLayout).release();
+ }
+
+ @Test
+ public void testOnDisplayAdded() {
+ mController.onDisplayAdded(DISPLAY_ID);
+ mController.onDisplayAdded(DISPLAY_ID + 1);
+
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID), any());
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID + 1), any());
+ }
+
+ @Test
+ public void testOnDisplayRemoved() {
+ mController.onDisplayAdded(DISPLAY_ID);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
+ mMockTaskListener);
+
+ mController.onDisplayRemoved(DISPLAY_ID + 1);
+
+ verify(mMockLayout, never()).release();
+ verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
+ any());
+
+ mController.onDisplayRemoved(DISPLAY_ID);
+
+ verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
+ verify(mMockLayout).release();
+ }
+
+ @Test
+ public void testOnDisplayConfigurationChanged() {
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+
+ mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
+
+ verify(mMockLayout, never()).updateDisplayLayout(any());
+
+ mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
+
+ verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+ }
+
+ @Test
+ public void testInsetsChanged() {
+ mController.onDisplayAdded(DISPLAY_ID);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+ InsetsState insetsState = new InsetsState();
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ insetsSource.setFrame(0, 0, 1000, 1000);
+ insetsState.addSource(insetsSource);
+
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID),
+ mOnInsetsChangedListenerCaptor.capture());
+ mOnInsetsChangedListenerCaptor.getValue().insetsChanged(insetsState);
+
+ verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+
+ // No update if the insets state is the same.
+ clearInvocations(mMockLayout);
+ mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
+ verify(mMockLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+ }
+
+ @Test
+ public void testChangeButtonVisibilityOnImeShowHide() {
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+
+ // Verify that the restart button is hidden after IME is showing.
+ mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
+
+ verify(mMockLayout).updateVisibility(false);
+
+ // Verify button remains hidden while IME is showing.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Verify button is shown after IME is hidden.
+ mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
+
+ verify(mMockLayout).updateVisibility(true);
+ }
+
+ @Test
+ public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+
+ // Verify that the restart button is hidden after keyguard becomes occluded.
+ mController.onKeyguardOccludedChanged(true);
+
+ verify(mMockLayout).updateVisibility(false);
+
+ // Verify button remains hidden while keyguard is occluded.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Verify button is shown after keyguard becomes not occluded.
+ mController.onKeyguardOccludedChanged(false);
+
+ verify(mMockLayout).updateVisibility(true);
+ }
+
+ @Test
+ public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+
+ mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
+ mController.onKeyguardOccludedChanged(true);
+
+ verify(mMockLayout, times(2)).updateVisibility(false);
+
+ clearInvocations(mMockLayout);
+
+ // Verify button remains hidden after keyguard becomes not occluded since IME is showing.
+ mController.onKeyguardOccludedChanged(false);
+
+ verify(mMockLayout).updateVisibility(false);
+
+ // Verify button is shown after IME is not showing.
+ mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
+
+ verify(mMockLayout).updateVisibility(true);
+ }
+
+ @Test
+ public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+
+ mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
+ mController.onKeyguardOccludedChanged(true);
+
+ verify(mMockLayout, times(2)).updateVisibility(false);
+
+ clearInvocations(mMockLayout);
+
+ // Verify button remains hidden after IME is hidden since keyguard is occluded.
+ mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
+
+ verify(mMockLayout).updateVisibility(false);
+
+ // Verify button is shown after keyguard becomes not occluded.
+ mController.onKeyguardOccludedChanged(false);
+
+ verify(mMockLayout).updateVisibility(true);
+ }
+
+ private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.displayId = displayId;
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
new file mode 100644
index 0000000..353d8fe
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.wm.shell.compatui;
+
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.SurfaceControlViewHost;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUILayout}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:CompatUILayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUILayoutTest extends ShellTestCase {
+
+ private static final int TASK_ID = 1;
+
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private CompatUIController.CompatUICallback mCallback;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private SurfaceControlViewHost mViewHost;
+
+ private CompatUIWindowManager mWindowManager;
+ private CompatUILayout mCompatUILayout;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
+ mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
+ false /* hasShownSizeCompatHint */, false /* hasShownCameraCompatHint */);
+
+ mCompatUILayout = (CompatUILayout)
+ LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
+ mCompatUILayout.inject(mWindowManager);
+
+ spyOn(mWindowManager);
+ spyOn(mCompatUILayout);
+ doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ }
+
+ @Test
+ public void testOnClickForRestartButton() {
+ final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
+ button.performClick();
+
+ verify(mWindowManager).onRestartButtonClicked();
+ verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+ }
+
+ @Test
+ public void testOnLongClickForRestartButton() {
+ doNothing().when(mWindowManager).onRestartButtonLongClicked();
+
+ final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onRestartButtonLongClicked();
+ }
+
+ @Test
+ public void testOnClickForSizeCompatHint() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+ final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
+ sizeCompatHint.performClick();
+
+ verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
+ }
+
+ @Test
+ public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraTreatmentButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ button.performClick();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ }
+
+ @Test
+ public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraTreatmentButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ button.performClick();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
+ public void testOnCameraDismissButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraDismissButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
+ }
+
+ @Test
+ public void testOnLongClickForCameraTreatementButton() {
+ doNothing().when(mWindowManager).onCameraButtonLongClicked();
+
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onCameraButtonLongClicked();
+ }
+
+ @Test
+ public void testOnLongClickForCameraDismissButton() {
+ doNothing().when(mWindowManager).onCameraButtonLongClicked();
+
+ final ImageButton button = mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onCameraButtonLongClicked();
+ }
+
+ @Test
+ public void testOnClickForCameraCompatHint() {
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final LinearLayout hint = mCompatUILayout.findViewById(R.id.camera_compat_hint);
+ hint.performClick();
+
+ verify(mCompatUILayout).setCameraCompatHintVisibility(/* show= */ false);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
new file mode 100644
index 0000000..11c7973
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -0,0 +1,368 @@
+/*
+ * 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.wm.shell.compatui;
+
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.view.DisplayInfo;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUIWindowManager}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:CompatUIWindowManagerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUIWindowManagerTest extends ShellTestCase {
+
+ private static final int TASK_ID = 1;
+
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private CompatUIController.CompatUICallback mCallback;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private CompatUILayout mCompatUILayout;
+ @Mock private SurfaceControlViewHost mViewHost;
+ private Configuration mTaskConfig;
+
+ private CompatUIWindowManager mWindowManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTaskConfig = new Configuration();
+
+ mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
+ mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
+ false /* hasShownSizeCompatHint */, false /* hasShownSizeCompatHint */);
+
+ spyOn(mWindowManager);
+ doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ }
+
+ @Test
+ public void testCreateSizeCompatButton() {
+ // Not create layout if show is false.
+ mWindowManager.createLayout(false /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mWindowManager, never()).inflateCompatUILayout();
+
+ // Not create hint popup.
+ mWindowManager.mShouldShowSizeCompatHint = false;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
+
+ // Create hint popup.
+ mWindowManager.release();
+ mWindowManager.mShouldShowSizeCompatHint = true;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mWindowManager, times(2)).inflateCompatUILayout();
+ assertNotNull(mCompatUILayout);
+ verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
+ assertFalse(mWindowManager.mShouldShowSizeCompatHint);
+ }
+
+ @Test
+ public void testRelease() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mWindowManager).inflateCompatUILayout();
+
+ mWindowManager.release();
+
+ verify(mViewHost).release();
+ }
+
+ @Test
+ public void testUpdateCompatInfo() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // No diff
+ clearInvocations(mWindowManager);
+ mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mWindowManager, never()).updateSurfacePosition();
+ verify(mWindowManager, never()).release();
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+
+ // Change task listener, recreate button.
+ clearInvocations(mWindowManager);
+ final ShellTaskOrganizer.TaskListener newTaskListener = mock(
+ ShellTaskOrganizer.TaskListener.class);
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mWindowManager).release();
+ verify(mWindowManager).createLayout(anyBoolean(), anyBoolean(), anyInt());
+
+ // Change Camera Compat state, show a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ // Change Camera Compat state, update a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ // Change Camera Compat state to hidden, hide a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
+
+ // Change task bounds, update position.
+ clearInvocations(mWindowManager);
+ final Configuration newTaskConfiguration = new Configuration();
+ newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
+ mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mWindowManager).updateSurfacePosition();
+ }
+
+ @Test
+ public void testUpdateDisplayLayout() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 1000;
+ displayInfo.logicalHeight = 2000;
+ final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+
+ mWindowManager.updateDisplayLayout(displayLayout1);
+ verify(mWindowManager).updateSurfacePosition();
+
+ // No update if the display bounds is the same.
+ clearInvocations(mWindowManager);
+ final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+ mWindowManager.updateDisplayLayout(displayLayout2);
+ verify(mWindowManager, never()).updateSurfacePosition();
+ }
+
+ @Test
+ public void testUpdateDisplayLayoutInsets() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 1000;
+ displayInfo.logicalHeight = 2000;
+ final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
+
+ mWindowManager.updateDisplayLayout(displayLayout);
+ verify(mWindowManager).updateSurfacePosition();
+
+ // Update if the insets change on the existing display layout
+ clearInvocations(mWindowManager);
+ InsetsState insetsState = new InsetsState();
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ insetsSource.setFrame(0, 0, 1000, 1000);
+ insetsState.addSource(insetsSource);
+ displayLayout.setInsets(mContext.getResources(), insetsState);
+ mWindowManager.updateDisplayLayout(displayLayout);
+ verify(mWindowManager).updateSurfacePosition();
+ }
+
+ @Test
+ public void testUpdateVisibility() {
+ // Create button if it is not created.
+ mWindowManager.mCompatUILayout = null;
+ mWindowManager.mHasSizeCompat = true;
+ mWindowManager.updateVisibility(true /* show */);
+
+ verify(mWindowManager).createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Hide button.
+ clearInvocations(mWindowManager);
+ doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
+ mWindowManager.updateVisibility(false /* show */);
+
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mCompatUILayout).setVisibility(View.GONE);
+
+ // Show button.
+ doReturn(View.GONE).when(mCompatUILayout).getVisibility();
+ mWindowManager.updateVisibility(true /* show */);
+
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mCompatUILayout).setVisibility(View.VISIBLE);
+ }
+
+ @Test
+ public void testAttachToParentSurface() {
+ final SurfaceControl.Builder b = new SurfaceControl.Builder();
+ mWindowManager.attachToParentSurface(b);
+
+ verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
+ }
+
+ @Test
+ public void testOnCameraDismissButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ clearInvocations(mCompatUILayout);
+ mWindowManager.onCameraDismissButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
+ verify(mCompatUILayout).setCameraControlVisibility(/* show= */ false);
+ }
+
+ @Test
+ public void testOnCameraTreatmentButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ clearInvocations(mCompatUILayout);
+ mWindowManager.onCameraTreatmentButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ mWindowManager.onCameraTreatmentButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
+ public void testOnRestartButtonClicked() {
+ mWindowManager.onRestartButtonClicked();
+
+ verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+ }
+
+ @Test
+ public void testOnRestartButtonLongClicked_showHint() {
+ // Not create hint popup.
+ mWindowManager.mShouldShowSizeCompatHint = false;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
+
+ mWindowManager.onRestartButtonLongClicked();
+
+ verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
+ }
+
+ @Test
+ public void testOnCamerControlLongClicked_showHint() {
+ // Not create hint popup.
+ mWindowManager.mShouldShowCameraCompatHint = false;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
+
+ mWindowManager.onCameraButtonLongClicked();
+
+ verify(mCompatUILayout).setCameraCompatHintVisibility(true /* show */);
+ }
+
+ @Test
+ public void testCreateCameraCompatControl() {
+ // Not create layout if show is false.
+ mWindowManager.createLayout(false /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager, never()).inflateCompatUILayout();
+
+ // Not create hint popup.
+ mWindowManager.mShouldShowCameraCompatHint = false;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
+ verify(mCompatUILayout).setCameraControlVisibility(true /* show */);
+
+ // Create hint popup.
+ mWindowManager.release();
+ mWindowManager.mShouldShowCameraCompatHint = true;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager, times(2)).inflateCompatUILayout();
+ assertNotNull(mCompatUILayout);
+ verify(mCompatUILayout, times(2)).setCameraControlVisibility(true /* show */);
+ assertFalse(mWindowManager.mShouldShowCameraCompatHint);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index bfa2c92..9f74520 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -30,7 +30,9 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -59,8 +61,8 @@
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
-
- mController = new DragAndDropController(mContext, mDisplayController, mUiEventLogger);
+ mController = new DragAndDropController(mContext, mDisplayController, mUiEventLogger,
+ mock(IconProvider.class), mock(ShellExecutor.class));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 734b97b..fe66e22 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -24,16 +24,14 @@
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+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 static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -149,7 +147,6 @@
mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
ACTIVITY_TYPE_STANDARD);
- setInSplitScreen(false);
setRunningTask(mFullscreenAppTask);
}
@@ -198,10 +195,6 @@
: ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
}
- private void setInSplitScreen(boolean inSplitscreen) {
- doReturn(inSplitscreen).when(mSplitScreenStarter).isSplitScreenVisible();
- }
-
@Test
public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
setRunningTask(mHomeTask);
@@ -211,7 +204,7 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
+ eq(SPLIT_POSITION_UNDEFINED), any());
}
@Test
@@ -223,12 +216,12 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -240,64 +233,12 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
- }
-
- @Test
- public void testDragAppOverSplitApp_expectSplitTargets_DropLeft() {
- setInSplitScreen(true);
- setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
- ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
-
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
- }
-
- @Test
- public void testDragAppOverSplitApp_expectSplitTargets_DropRight() {
- setInSplitScreen(true);
- setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
- ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
-
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
- }
-
- @Test
- public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropTop() {
- setInSplitScreen(true);
- setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
- ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
-
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
- }
-
- @Test
- public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropBottom() {
- setInSplitScreen(true);
- setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
- ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
-
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index a6215d3..8e30f65 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -188,7 +188,7 @@
final Rect newBounds = new Rect(50, 50, 100, 75);
mPipBoundsState.setBounds(currentBounds);
- mPipBoundsState.setPipExclusionBoundsChangeCallback(callback);
+ mPipBoundsState.addPipExclusionBoundsChangeCallback(callback);
// Setting the listener immediately calls back with the current bounds.
verify(callback).accept(currentBounds);
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 19a5417..50f6bd7 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
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static java.lang.Integer.MAX_VALUE;
@@ -83,6 +84,36 @@
}
@Test
+ public void testAddRemoveSplitNotifyChange() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ setRawList(t1, t2);
+
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, mock(StagedSplitBounds.class));
+ verify(mRecentTasksController).notifyRecentTasksChanged();
+
+ reset(mRecentTasksController);
+ mRecentTasksController.removeSplitPair(t1.taskId);
+ verify(mRecentTasksController).notifyRecentTasksChanged();
+ }
+
+ @Test
+ public void testAddSameSplitBoundsInfoSkipNotifyChange() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ setRawList(t1, t2);
+
+ // Verify only one update if the split info is the same
+ StagedSplitBounds bounds1 = new StagedSplitBounds(new Rect(0, 0, 50, 50),
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds1);
+ StagedSplitBounds bounds2 = new StagedSplitBounds(new Rect(0, 0, 50, 50),
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds2);
+ verify(mRecentTasksController, times(1)).notifyRecentTasksChanged();
+ }
+
+ @Test
public void testGetRecentTasks() {
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
deleted file mode 100644
index 3a14a33..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatHintPopup}.
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:SizeCompatHintPopupTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatHintPopupTest extends ShellTestCase {
-
- @Mock private SyncTransactionQueue mSyncTransactionQueue;
- @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
- @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
- @Mock private DisplayLayout mDisplayLayout;
-
- private SizeCompatUILayout mLayout;
- private SizeCompatHintPopup mHint;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- final int taskId = 1;
- mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
- new Configuration(), taskId, mTaskListener, mDisplayLayout,
- false /* hasShownHint */);
- mHint = (SizeCompatHintPopup)
- LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
- mHint.inject(mLayout);
-
- spyOn(mLayout);
- }
-
- @Test
- public void testOnClick() {
- doNothing().when(mLayout).dismissHint();
-
- final LinearLayout hintPopup = mHint.findViewById(R.id.size_compat_hint_popup);
- hintPopup.performClick();
-
- verify(mLayout).dismissHint();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
deleted file mode 100644
index a20a5e9..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-import android.widget.ImageButton;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatRestartButton}.
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:SizeCompatRestartButtonTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatRestartButtonTest extends ShellTestCase {
-
- private static final int TASK_ID = 1;
-
- @Mock private SyncTransactionQueue mSyncTransactionQueue;
- @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
- @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
- @Mock private DisplayLayout mDisplayLayout;
-
- private SizeCompatUILayout mLayout;
- private SizeCompatRestartButton mButton;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
- new Configuration(), TASK_ID, mTaskListener, mDisplayLayout,
- false /* hasShownHint */);
- mButton = (SizeCompatRestartButton)
- LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
- mButton.inject(mLayout);
-
- spyOn(mLayout);
- }
-
- @Test
- public void testOnClick() {
- final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
- button.performClick();
-
- verify(mLayout).onRestartButtonClicked();
- verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
- }
-
- @Test
- public void testOnLongClick() {
- doNothing().when(mLayout).onRestartButtonLongClicked();
-
- final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
- button.performLongClick();
-
- verify(mLayout).onRestartButtonLongClicked();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
deleted file mode 100644
index 877b192..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatUIController}.
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:SizeCompatUIControllerTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatUIControllerTest extends ShellTestCase {
- private static final int DISPLAY_ID = 0;
- private static final int TASK_ID = 12;
-
- private SizeCompatUIController mController;
- private @Mock DisplayController mMockDisplayController;
- private @Mock DisplayInsetsController mMockDisplayInsetsController;
- private @Mock DisplayLayout mMockDisplayLayout;
- private @Mock DisplayImeController mMockImeController;
- private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
- private @Mock SyncTransactionQueue mMockSyncQueue;
- private @Mock ShellExecutor mMockExecutor;
- private @Mock SizeCompatUILayout mMockLayout;
-
- @Captor
- ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
- doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
- doReturn(TASK_ID).when(mMockLayout).getTaskId();
- mController = new SizeCompatUIController(mContext, mMockDisplayController,
- mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
- @Override
- SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
- Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
- return mMockLayout;
- }
- };
- spyOn(mController);
- }
-
- @Test
- public void testListenerRegistered() {
- verify(mMockDisplayController).addDisplayWindowListener(mController);
- verify(mMockImeController).addPositionProcessor(mController);
- }
-
- @Test
- public void testOnSizeCompatInfoChanged() {
- final Configuration taskConfig = new Configuration();
-
- // Verify that the restart button is added with non-null size compat info.
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
-
- verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
- eq(mMockTaskListener));
-
- // Verify that the restart button is updated with non-null new size compat info.
- final Configuration newTaskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
-
- verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
- true /* show */);
-
- // Verify that the restart button is removed with null size compat info.
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
-
- verify(mMockLayout).release();
- }
-
- @Test
- public void testOnDisplayAdded() {
- mController.onDisplayAdded(DISPLAY_ID);
- mController.onDisplayAdded(DISPLAY_ID + 1);
-
- verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID), any());
- verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID + 1), any());
- }
-
- @Test
- public void testOnDisplayRemoved() {
- mController.onDisplayAdded(DISPLAY_ID);
- final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
-
- mController.onDisplayRemoved(DISPLAY_ID + 1);
-
- verify(mMockLayout, never()).release();
- verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
- any());
-
- mController.onDisplayRemoved(DISPLAY_ID);
-
- verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
- verify(mMockLayout).release();
- }
-
- @Test
- public void testOnDisplayConfigurationChanged() {
- final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
-
- final Configuration newTaskConfig = new Configuration();
- mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, newTaskConfig);
-
- verify(mMockLayout, never()).updateDisplayLayout(any());
-
- mController.onDisplayConfigurationChanged(DISPLAY_ID, newTaskConfig);
-
- verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
- }
-
- @Test
- public void testInsetsChanged() {
- mController.onDisplayAdded(DISPLAY_ID);
- final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
- InsetsState insetsState = new InsetsState();
- InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
- insetsSource.setFrame(0, 0, 1000, 1000);
- insetsState.addSource(insetsSource);
-
- verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID),
- mOnInsetsChangedListenerCaptor.capture());
- mOnInsetsChangedListenerCaptor.getValue().insetsChanged(insetsState);
-
- verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
-
- // No update if the insets state is the same.
- clearInvocations(mMockLayout);
- mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
- verify(mMockLayout, never()).updateDisplayLayout(mMockDisplayLayout);
- }
-
- @Test
- public void testChangeButtonVisibilityOnImeShowHide() {
- final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
-
- // Verify that the restart button is hidden after IME is showing.
- mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
-
- verify(mMockLayout).updateVisibility(false);
-
- // Verify button remains hidden while IME is showing.
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
-
- verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
- false /* show */);
-
- // Verify button is shown after IME is hidden.
- mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
-
- verify(mMockLayout).updateVisibility(true);
- }
-
- @Test
- public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
- final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
-
- // Verify that the restart button is hidden after keyguard becomes occluded.
- mController.onKeyguardOccludedChanged(true);
-
- verify(mMockLayout).updateVisibility(false);
-
- // Verify button remains hidden while keyguard is occluded.
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
-
- verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
- false /* show */);
-
- // Verify button is shown after keyguard becomes not occluded.
- mController.onKeyguardOccludedChanged(false);
-
- verify(mMockLayout).updateVisibility(true);
- }
-
- @Test
- public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
- final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
-
- mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
- mController.onKeyguardOccludedChanged(true);
-
- verify(mMockLayout, times(2)).updateVisibility(false);
-
- clearInvocations(mMockLayout);
-
- // Verify button remains hidden after keyguard becomes not occluded since IME is showing.
- mController.onKeyguardOccludedChanged(false);
-
- verify(mMockLayout).updateVisibility(false);
-
- // Verify button is shown after IME is not showing.
- mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
-
- verify(mMockLayout).updateVisibility(true);
- }
-
- @Test
- public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
- final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
-
- mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
- mController.onKeyguardOccludedChanged(true);
-
- verify(mMockLayout, times(2)).updateVisibility(false);
-
- clearInvocations(mMockLayout);
-
- // Verify button remains hidden after IME is hidden since keyguard is occluded.
- mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
-
- verify(mMockLayout).updateVisibility(false);
-
- // Verify button is shown after keyguard becomes not occluded.
- mController.onKeyguardOccludedChanged(false);
-
- verify(mMockLayout).updateVisibility(true);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
deleted file mode 100644
index eb9305b..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
-import android.view.DisplayInfo;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatUILayout}.
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:SizeCompatUILayoutTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatUILayoutTest extends ShellTestCase {
-
- private static final int TASK_ID = 1;
-
- @Mock private SyncTransactionQueue mSyncTransactionQueue;
- @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
- @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
- @Mock private DisplayLayout mDisplayLayout;
- @Mock private SizeCompatRestartButton mButton;
- @Mock private SizeCompatHintPopup mHint;
- private Configuration mTaskConfig;
-
- private SizeCompatUILayout mLayout;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mTaskConfig = new Configuration();
-
- mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
- new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
- false /* hasShownHint */);
-
- spyOn(mLayout);
- spyOn(mLayout.mButtonWindowManager);
- doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton();
-
- final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager();
- spyOn(hintWindowManager);
- doReturn(mHint).when(hintWindowManager).createSizeCompatHint();
- doReturn(hintWindowManager).when(mLayout).createHintWindowManager();
- }
-
- @Test
- public void testCreateSizeCompatButton() {
- // Not create button if show is false.
- mLayout.createSizeCompatButton(false /* show */);
-
- verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton();
- assertNull(mLayout.mButton);
- assertNull(mLayout.mHintWindowManager);
- assertNull(mLayout.mHint);
-
- // Not create hint popup.
- mLayout.mShouldShowHint = false;
- mLayout.createSizeCompatButton(true /* show */);
-
- verify(mLayout.mButtonWindowManager).createSizeCompatButton();
- assertNotNull(mLayout.mButton);
- assertNull(mLayout.mHintWindowManager);
- assertNull(mLayout.mHint);
-
- // Create hint popup.
- mLayout.release();
- mLayout.mShouldShowHint = true;
- mLayout.createSizeCompatButton(true /* show */);
-
- verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton();
- assertNotNull(mLayout.mButton);
- assertNotNull(mLayout.mHintWindowManager);
- verify(mLayout.mHintWindowManager).createSizeCompatHint();
- assertNotNull(mLayout.mHint);
- assertFalse(mLayout.mShouldShowHint);
- }
-
- @Test
- public void testRelease() {
- mLayout.createSizeCompatButton(true /* show */);
- final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
-
- mLayout.release();
-
- assertNull(mLayout.mButton);
- assertNull(mLayout.mHint);
- verify(hintWindowManager).release();
- assertNull(mLayout.mHintWindowManager);
- verify(mLayout.mButtonWindowManager).release();
- }
-
- @Test
- public void testUpdateSizeCompatInfo() {
- mLayout.createSizeCompatButton(true /* show */);
-
- // No diff
- clearInvocations(mLayout);
- mLayout.updateSizeCompatInfo(mTaskConfig, mTaskListener, true /* show */);
-
- verify(mLayout, never()).updateButtonSurfacePosition();
- verify(mLayout, never()).release();
- verify(mLayout, never()).createSizeCompatButton(anyBoolean());
-
- // Change task listener, recreate button.
- clearInvocations(mLayout);
- final ShellTaskOrganizer.TaskListener newTaskListener = mock(
- ShellTaskOrganizer.TaskListener.class);
- mLayout.updateSizeCompatInfo(mTaskConfig, newTaskListener,
- true /* show */);
-
- verify(mLayout).release();
- verify(mLayout).createSizeCompatButton(anyBoolean());
-
- // Change task bounds, update position.
- clearInvocations(mLayout);
- final Configuration newTaskConfiguration = new Configuration();
- newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
- mLayout.updateSizeCompatInfo(newTaskConfiguration, newTaskListener,
- true /* show */);
-
- verify(mLayout).updateButtonSurfacePosition();
- verify(mLayout).updateHintSurfacePosition();
- }
-
- @Test
- public void testUpdateDisplayLayout() {
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.logicalWidth = 1000;
- displayInfo.logicalHeight = 2000;
- final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
- mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
-
- mLayout.updateDisplayLayout(displayLayout1);
- verify(mLayout).updateButtonSurfacePosition();
- verify(mLayout).updateHintSurfacePosition();
-
- // No update if the display bounds is the same.
- clearInvocations(mLayout);
- final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
- mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
- mLayout.updateDisplayLayout(displayLayout2);
- verify(mLayout, never()).updateButtonSurfacePosition();
- verify(mLayout, never()).updateHintSurfacePosition();
- }
-
- @Test
- public void testUpdateDisplayLayoutInsets() {
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.logicalWidth = 1000;
- displayInfo.logicalHeight = 2000;
- final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
- mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
-
- mLayout.updateDisplayLayout(displayLayout);
- verify(mLayout).updateButtonSurfacePosition();
- verify(mLayout).updateHintSurfacePosition();
-
- // Update if the insets change on the existing display layout
- clearInvocations(mLayout);
- InsetsState insetsState = new InsetsState();
- InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
- insetsSource.setFrame(0, 0, 1000, 1000);
- insetsState.addSource(insetsSource);
- displayLayout.setInsets(mContext.getResources(), insetsState);
- mLayout.updateDisplayLayout(displayLayout);
- verify(mLayout).updateButtonSurfacePosition();
- verify(mLayout).updateHintSurfacePosition();
- }
-
- @Test
- public void testUpdateVisibility() {
- // Create button if it is not created.
- mLayout.mButton = null;
- mLayout.updateVisibility(true /* show */);
-
- verify(mLayout).createSizeCompatButton(true /* show */);
-
- // Hide button.
- clearInvocations(mLayout);
- doReturn(View.VISIBLE).when(mButton).getVisibility();
- mLayout.updateVisibility(false /* show */);
-
- verify(mLayout, never()).createSizeCompatButton(anyBoolean());
- verify(mButton).setVisibility(View.GONE);
-
- // Show button.
- doReturn(View.GONE).when(mButton).getVisibility();
- mLayout.updateVisibility(true /* show */);
-
- verify(mLayout, never()).createSizeCompatButton(anyBoolean());
- verify(mButton).setVisibility(View.VISIBLE);
- }
-
- @Test
- public void testAttachToParentSurface() {
- final SurfaceControl.Builder b = new SurfaceControl.Builder();
- mLayout.attachToParentSurface(b);
-
- verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
- }
-
- @Test
- public void testOnRestartButtonClicked() {
- mLayout.onRestartButtonClicked();
-
- verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
- }
-
- @Test
- public void testOnRestartButtonLongClicked_showHint() {
- mLayout.dismissHint();
-
- assertNull(mLayout.mHint);
-
- mLayout.onRestartButtonLongClicked();
-
- assertNotNull(mLayout.mHint);
- }
-
- @Test
- public void testDismissHint() {
- mLayout.onRestartButtonLongClicked();
- final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
- assertNotNull(mLayout.mHint);
- assertNotNull(hintWindowManager);
-
- mLayout.dismissHint();
-
- assertNull(mLayout.mHint);
- assertNull(mLayout.mHintWindowManager);
- verify(hintWindowManager).release();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index ad65c04..85f6789 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -19,16 +19,23 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+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 static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -49,7 +56,6 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -103,19 +109,47 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mStageCoordinator = createStageCoordinator(/* splitLayout */ null);
+ mStageCoordinator = spy(createStageCoordinator(/* splitLayout */ null));
+ doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
}
@Test
- public void testMoveToSideStage() {
+ public void testMoveToStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
- mStageCoordinator.moveToSideStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ mStageCoordinator.moveToStage(task, STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ new WindowContainerTransaction());
+ verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
+ assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
+ mStageCoordinator.moveToStage(task, STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ new WindowContainerTransaction());
verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
+ assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
+ }
+
+ @Test
+ public void testMoveToUndefinedStage() {
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+
+ // Verify move to undefined stage while split screen not activated moves task to side stage.
+ when(mMainStage.isActive()).thenReturn(false);
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
+ mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ new WindowContainerTransaction());
+ verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
+ assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
+
+ // Verify move to undefined stage after split screen activated moves task based on position.
+ when(mMainStage.isActive()).thenReturn(true);
+ assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
+ mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
+ new WindowContainerTransaction());
+ verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
+ assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
}
@Test
@@ -195,6 +229,63 @@
verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
}
+ @Test
+ public void testResolveStartStage_beforeSplitActivated_setsStagePosition() {
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ verify(mStageCoordinator).updateActivityOptions(any(), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT));
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_TOP_OR_LEFT);
+ verify(mStageCoordinator).updateActivityOptions(any(), eq(SPLIT_POSITION_TOP_OR_LEFT));
+ }
+
+ @Test
+ public void testResolveStartStage_afterSplitActivated_retrievesStagePosition() {
+ when(mMainStage.isActive()).thenReturn(true);
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_TOP_OR_LEFT);
+ verify(mStageCoordinator).updateActivityOptions(any(), eq(SPLIT_POSITION_TOP_OR_LEFT));
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getMainStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ verify(mStageCoordinator).updateActivityOptions(any(), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT));
+ }
+
+ @Test
+ public void testResolveStartStage_setsSideStagePosition() {
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getMainStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ }
+
+ @Test
+ public void testResolveStartStage_retrievesStagePosition() {
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_SIDE, SPLIT_POSITION_UNDEFINED,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_TOP_OR_LEFT);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN, SPLIT_POSITION_UNDEFINED,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getMainStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ }
+
private StageCoordinator createStageCoordinator(SplitLayout splitLayout) {
return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 70b7c67..d92b12e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -31,6 +31,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -117,16 +118,19 @@
WindowManager.LayoutParams params, int suggestType) {
// listen for addView
mAddWindowForTask = taskId;
+ saveSplashScreenRecord(appToken, taskId, view, suggestType);
// Do not wait for background color
return false;
}
@Override
- protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo) {
+ protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo,
+ boolean immediately) {
// listen for removeView
if (mAddWindowForTask == removalInfo.taskId) {
mAddWindowForTask = 0;
}
+ mStartingWindowRecords.remove(removalInfo.taskId);
}
}
@@ -179,7 +183,7 @@
removalInfo.taskId = windowInfo.taskInfo.taskId;
mStartingSurfaceDrawer.removeStartingWindow(removalInfo);
waitHandlerIdle(mTestHandler);
- verify(mStartingSurfaceDrawer).removeWindowSynced(any());
+ verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(false));
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
}
@@ -267,11 +271,32 @@
// Verify the task snapshot with IME snapshot will be removed when received the real IME
// drawn callback.
+ // makeTaskSnapshotWindow shall call removeWindowSynced before there add a new
+ // StartingWindowRecord for the task.
mStartingSurfaceDrawer.onImeDrawnOnTask(1);
- verify(mockSnapshotWindow).removeImmediately();
+ verify(mStartingSurfaceDrawer, times(2))
+ .removeWindowSynced(any(), eq(true));
}
}
+ @Test
+ public void testClearAllWindows() {
+ final int taskId = 1;
+ final StartingWindowInfo windowInfo =
+ createWindowInfo(taskId, android.R.style.Theme);
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
+ STARTING_WINDOW_TYPE_SPLASH_SCREEN);
+ waitHandlerIdle(mTestHandler);
+ verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(),
+ eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
+ assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
+
+ mStartingSurfaceDrawer.clearAllWindows();
+ waitHandlerIdle(mTestHandler);
+ verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(true));
+ assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0);
+ }
+
private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
StartingWindowInfo windowInfo = new StartingWindowInfo();
final ActivityInfo info = new ActivityInfo();
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index d17c328..13aff38 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -384,7 +384,16 @@
return base::unexpected(IOError::PAGES_MISSING);
}
- auto offset = dtohl(entry_offset_ptr.value());
+ uint32_t offset;
+ uint16_t res_idx;
+ if (type->flags & ResTable_type::FLAG_SPARSE) {
+ auto sparse_entry = entry_offset_ptr.convert<ResTable_sparseTypeEntry>();
+ offset = dtohs(sparse_entry->offset) * 4u;
+ res_idx = dtohs(sparse_entry->idx);
+ } else {
+ offset = dtohl(entry_offset_ptr.value());
+ res_idx = entry_idx;
+ }
if (offset != ResTable_type::NO_ENTRY) {
auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
if (!entry) {
@@ -394,7 +403,7 @@
if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
- return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx);
+ return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx);
}
}
}
@@ -686,6 +695,12 @@
std::unordered_set<uint32_t> finalized_ids;
const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
if (!lib_alias) {
+ LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small.";
+ return {};
+ }
+ if ((child_chunk.data_size() / sizeof(ResTable_staged_alias_entry))
+ < dtohl(lib_alias->count)) {
+ LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small to hold entries.";
return {};
}
const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index f356c8130..d214e2d 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -95,6 +95,38 @@
ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
}
+TEST(LoadedArscTest, FindSparseEntryApp) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
+ &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(sparse::R::string::only_v26));
+ ASSERT_THAT(package, NotNull());
+
+ const uint8_t type_index = get_type_id(sparse::R::string::only_v26) - 1;
+ const uint16_t entry_index = get_entry_id(sparse::R::string::only_v26);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
+
+ // Ensure that AAPT2 sparsely encoded the v26 config as expected.
+ auto type_entry = std::find_if(
+ type_spec->type_entries.begin(), type_spec->type_entries.end(),
+ [](const TypeSpec::TypeEntry& x) { return x.config.sdkVersion == 26; });
+ ASSERT_NE(type_entry, type_spec->type_entries.end());
+ ASSERT_NE(type_entry->type->flags & ResTable_type::FLAG_SPARSE, 0);
+
+ // Test fetching a resource with only sparsely encoded configs by name.
+ auto id = package->FindEntryByName(u"string", u"only_v26");
+ ASSERT_EQ(id.value(), fix_package_id(sparse::R::string::only_v26, 0));
+}
+
TEST(LoadedArscTest, LoadSharedLibrary) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
diff --git a/libs/androidfw/tests/data/sparse/R.h b/libs/androidfw/tests/data/sparse/R.h
index 243e74f..2492dbf 100644
--- a/libs/androidfw/tests/data/sparse/R.h
+++ b/libs/androidfw/tests/data/sparse/R.h
@@ -27,21 +27,22 @@
struct integer {
enum : uint32_t {
foo_0 = 0x7f010000,
- foo_1 = 0x7f010000,
- foo_2 = 0x7f010000,
- foo_3 = 0x7f010000,
- foo_4 = 0x7f010000,
- foo_5 = 0x7f010000,
- foo_6 = 0x7f010000,
- foo_7 = 0x7f010000,
- foo_8 = 0x7f010000,
- foo_9 = 0x7f010000,
+ foo_1 = 0x7f010001,
+ foo_2 = 0x7f010002,
+ foo_3 = 0x7f010003,
+ foo_4 = 0x7f010004,
+ foo_5 = 0x7f010005,
+ foo_6 = 0x7f010006,
+ foo_7 = 0x7f010007,
+ foo_8 = 0x7f010008,
+ foo_9 = 0x7f010009,
};
};
struct string {
enum : uint32_t {
foo_999 = 0x7f0203e7,
+ only_v26 = 0x7f0203e8
};
};
};
diff --git a/libs/androidfw/tests/data/sparse/gen_strings.sh b/libs/androidfw/tests/data/sparse/gen_strings.sh
index e7e1d60..4ea5468 100755
--- a/libs/androidfw/tests/data/sparse/gen_strings.sh
+++ b/libs/androidfw/tests/data/sparse/gen_strings.sh
@@ -14,5 +14,7 @@
fi
done
echo "</resources>" >> $OUTPUT_default
+
+echo " <string name=\"only_v26\">only v26</string>" >> $OUTPUT_v26
echo "</resources>" >> $OUTPUT_v26
diff --git a/libs/androidfw/tests/data/sparse/not_sparse.apk b/libs/androidfw/tests/data/sparse/not_sparse.apk
index 599a370..b08a621 100644
--- a/libs/androidfw/tests/data/sparse/not_sparse.apk
+++ b/libs/androidfw/tests/data/sparse/not_sparse.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
index b6f8299..d116087e 100644
--- a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
+++ b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
@@ -333,4 +333,5 @@
<string name="foo_993">9930</string>
<string name="foo_996">9960</string>
<string name="foo_999">9990</string>
+ <string name="only_v26">only v26</string>
</resources>
diff --git a/libs/androidfw/tests/data/sparse/sparse.apk b/libs/androidfw/tests/data/sparse/sparse.apk
index 1f9bba3..9fd01fb 100644
--- a/libs/androidfw/tests/data/sparse/sparse.apk
+++ b/libs/androidfw/tests/data/sparse/sparse.apk
Binary files differ
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 54367b8..b5536ad 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -73,6 +73,10 @@
} gFrameDrawingCallback;
struct {
+ jmethodID onFrameCommit;
+} gFrameCommitCallback;
+
+struct {
jmethodID onFrameComplete;
} gFrameCompleteCallback;
@@ -101,22 +105,21 @@
JavaVM* mVm;
};
-class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
+class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> {
public:
- explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
+ explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) {
env->GetJavaVM(&mVm);
mObject = env->NewGlobalRef(jobject);
LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
}
- ~FrameCompleteWrapper() {
- releaseObject();
- }
+ ~FrameCommitWrapper() { releaseObject(); }
- void onFrameComplete(int64_t frameNr) {
+ void onFrameCommit(bool didProduceBuffer) {
if (mObject) {
- ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
- getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
+ ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer);
+ getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
+ didProduceBuffer);
releaseObject();
}
}
@@ -637,15 +640,33 @@
}
}
+static void android_view_ThreadedRenderer_setFrameCommitCallback(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jobject callback) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ if (!callback) {
+ proxy->setFrameCommitCallback(nullptr);
+ } else {
+ sp<FrameCommitWrapper> wrapper = new FrameCommitWrapper{env, callback};
+ proxy->setFrameCommitCallback(
+ [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); });
+ }
+}
+
static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
jobject clazz, jlong proxyPtr, jobject callback) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
if (!callback) {
proxy->setFrameCompleteCallback(nullptr);
} else {
- sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback};
- proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) {
- wrapper->onFrameComplete(frameNr);
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto globalCallbackRef =
+ std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
+ proxy->setFrameCompleteCallback([globalCallbackRef]() {
+ JNIEnv* env = getenv(globalCallbackRef->vm());
+ env->CallVoidMethod(globalCallbackRef->object(),
+ gFrameCompleteCallback.onFrameComplete);
});
}
}
@@ -929,6 +950,8 @@
(void*)android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback},
{"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
(void*)android_view_ThreadedRenderer_setFrameCallback},
+ {"nSetFrameCommitCallback", "(JLandroid/graphics/HardwareRenderer$FrameCommitCallback;)V",
+ (void*)android_view_ThreadedRenderer_setFrameCommitCallback},
{"nSetFrameCompleteCallback",
"(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V",
(void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
@@ -994,10 +1017,15 @@
gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
"onFrameDraw", "(J)V");
+ jclass frameCommitClass =
+ FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback");
+ gFrameCommitCallback.onFrameCommit =
+ GetMethodIDOrDie(env, frameCommitClass, "onFrameCommit", "(Z)V");
+
jclass frameCompleteClass = FindClassOrDie(env,
"android/graphics/HardwareRenderer$FrameCompleteCallback");
- gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass,
- "onFrameComplete", "(J)V");
+ gFrameCompleteCallback.onFrameComplete =
+ GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");
void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2f3a509..a066e6f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -491,10 +491,10 @@
// Notify the callbacks, even if there's nothing to draw so they aren't waiting
// indefinitely
waitOnFences();
- for (auto& func : mFrameCompleteCallbacks) {
- std::invoke(func, mFrameNumber);
+ for (auto& func : mFrameCommitCallbacks) {
+ std::invoke(func, false /* didProduceBuffer */);
}
- mFrameCompleteCallbacks.clear();
+ mFrameCommitCallbacks.clear();
return 0;
}
@@ -603,10 +603,10 @@
#endif
if (didSwap) {
- for (auto& func : mFrameCompleteCallbacks) {
- std::invoke(func, frameCompleteNr);
+ for (auto& func : mFrameCommitCallbacks) {
+ std::invoke(func, true /* didProduceBuffer */);
}
- mFrameCompleteCallbacks.clear();
+ mFrameCommitCallbacks.clear();
}
if (requireSwap) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6dbfcc3..9df429b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -187,8 +187,8 @@
IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); }
- void addFrameCompleteListener(std::function<void(int64_t)>&& func) {
- mFrameCompleteCallbacks.push_back(std::move(func));
+ void addFrameCommitListener(std::function<void(bool)>&& func) {
+ mFrameCommitCallbacks.push_back(std::move(func));
}
void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback) {
@@ -320,7 +320,7 @@
std::vector<std::future<void>> mFrameFences;
std::unique_ptr<IRenderPipeline> mRenderPipeline;
- std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;
+ std::vector<std::function<void(bool)>> mFrameCommitCallbacks;
// If set to true, we expect that callbacks into onSurfaceStatsAvailable
bool mExpectSurfaceStats = false;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index e7081df..94aedd0 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -150,16 +150,18 @@
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
- if (mFrameCompleteCallback) {
- mContext->addFrameCompleteListener(std::move(mFrameCompleteCallback));
- mFrameCompleteCallback = nullptr;
+ if (mFrameCommitCallback) {
+ mContext->addFrameCommitListener(std::move(mFrameCommitCallback));
+ mFrameCommitCallback = nullptr;
}
}
// Grab a copy of everything we need
CanvasContext* context = mContext;
- std::function<void(int64_t)> callback = std::move(mFrameCallback);
+ std::function<void(int64_t)> frameCallback = std::move(mFrameCallback);
+ std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
+ mFrameCompleteCallback = nullptr;
int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
@@ -170,9 +172,9 @@
}
// Even if we aren't drawing this vsync pulse the next frame number will still be accurate
- if (CC_UNLIKELY(callback)) {
+ if (CC_UNLIKELY(frameCallback)) {
context->enqueueFrameWork(
- [callback, frameNr = context->getFrameNumber()]() { callback(frameNr); });
+ [frameCallback, frameNr = context->getFrameNumber()]() { frameCallback(frameNr); });
}
nsecs_t dequeueBufferDuration = 0;
@@ -189,6 +191,10 @@
context->waitOnFences();
}
+ if (CC_UNLIKELY(frameCompleteCallback)) {
+ std::invoke(frameCompleteCallback);
+ }
+
if (!canUnblockUiThread) {
unblockUiThread();
}
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 6a61a2b..e3ea802 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -81,7 +81,11 @@
mFrameCallback = std::move(callback);
}
- void setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+ void setFrameCommitCallback(std::function<void(bool)>&& callback) {
+ mFrameCommitCallback = std::move(callback);
+ }
+
+ void setFrameCompleteCallback(std::function<void()>&& callback) {
mFrameCompleteCallback = std::move(callback);
}
@@ -123,7 +127,8 @@
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
std::function<void(int64_t)> mFrameCallback;
- std::function<void(int64_t)> mFrameCompleteCallback;
+ std::function<void(bool)> mFrameCommitCallback;
+ std::function<void()> mFrameCompleteCallback;
nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mLastTargetWorkDuration = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c485ce2..72d4ac5 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -326,7 +326,11 @@
mDrawFrameTask.setFrameCallback(std::move(callback));
}
-void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+void RenderProxy::setFrameCommitCallback(std::function<void(bool)>&& callback) {
+ mDrawFrameTask.setFrameCommitCallback(std::move(callback));
+}
+
+void RenderProxy::setFrameCompleteCallback(std::function<void()>&& callback) {
mDrawFrameTask.setFrameCompleteCallback(std::move(callback));
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 2b5405c..6417b38 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -124,7 +124,8 @@
const std::function<bool(int64_t, int64_t, int64_t)>& callback);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback);
void setFrameCallback(std::function<void(int64_t)>&& callback);
- void setFrameCompleteCallback(std::function<void(int64_t)>&& callback);
+ void setFrameCommitCallback(std::function<void(bool)>&& callback);
+ void setFrameCompleteCallback(std::function<void()>&& callback);
void addFrameMetricsObserver(FrameMetricsObserver* observer);
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 693a027..d818ffc 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -298,7 +298,7 @@
auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats;
- for (const auto& [surfaceControl, latchTime, acquireTime, presentFence, previousReleaseFence, transformHint, frameEvents] : surfaceControlStats) {
+ for (const auto& [surfaceControl, latchTime, acquireTime, presentFence, previousReleaseFence, transformHint, frameEvents, ignore] : surfaceControlStats) {
ASurfaceControl* aSurfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
aSurfaceControlStats[aSurfaceControl].acquireTime = acquireTime;
aSurfaceControlStats[aSurfaceControl].previousReleaseFence = previousReleaseFence;
@@ -650,7 +650,7 @@
for (const auto&
[surfaceControl, latchTime, acquireTime, presentFence,
previousReleaseFence, transformHint,
- frameEvents] : surfaceControlStats) {
+ frameEvents, ignore] : surfaceControlStats) {
ASurfaceControl* aSurfaceControl =
reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
aSurfaceControlStats[aSurfaceControl].acquireTime = acquireTime;
@@ -662,4 +662,4 @@
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
transaction->addTransactionCommittedCallback(callback, context);
-}
\ No newline at end of file
+}
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index ed1ec55..a992b86 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -4,11 +4,11 @@
<string name="app_label" msgid="8016145283189546017">"Dispositivi di immissione"</string>
<string name="keyboard_layouts_label" msgid="6688773268302087545">"Tastiera Android"</string>
<string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"Inglese (UK)"</string>
- <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"Inglese (USA)"</string>
- <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglese (USA), stile internazionale"</string>
- <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglese (USA), stile Colemak"</string>
- <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglese (USA), stile Dvorak"</string>
- <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglese (USA), stile Workman"</string>
+ <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"Inglese (US)"</string>
+ <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglese (US), stile internazionale"</string>
+ <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglese (US), stile Colemak"</string>
+ <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglese (US), stile Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Inglese (US), stile Workman"</string>
<string name="keyboard_layout_german_label" msgid="8451565865467909999">"Tedesco"</string>
<string name="keyboard_layout_french_label" msgid="813450119589383723">"Francese"</string>
<string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francese (Canada)"</string>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 25a3a90..4d18215 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -3,51 +3,51 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="8016145283189546017">"Urządzenia wejściowe"</string>
<string name="keyboard_layouts_label" msgid="6688773268302087545">"Klawiatura Android"</string>
- <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"Angielski (Wielka Brytania)"</string>
- <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"Angielski (USA)"</string>
- <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Angielski (USA), międzynarodowy"</string>
- <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Angielski (USA), Colemak"</string>
- <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Angielski (USA), Dvorak"</string>
- <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Angielski (USA), Workman"</string>
- <string name="keyboard_layout_german_label" msgid="8451565865467909999">"Niemiecki"</string>
- <string name="keyboard_layout_french_label" msgid="813450119589383723">"Francuski"</string>
- <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Francuski (Kanada)"</string>
- <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"Rosyjski"</string>
- <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"Rosyjski, Mac"</string>
- <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"Hiszpański"</string>
- <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"Francuski (Szwajcaria)"</string>
- <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Niemiecki (Szwajcaria)"</string>
- <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgijski"</string>
- <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bułgarski"</string>
- <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bułgarski (znaki fonetyczne)"</string>
- <string name="keyboard_layout_italian" msgid="6497079660449781213">"Włoski"</string>
- <string name="keyboard_layout_danish" msgid="8036432066627127851">"Duński"</string>
- <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norweski"</string>
- <string name="keyboard_layout_swedish" msgid="732959109088479351">"Szwedzki"</string>
- <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fiński"</string>
- <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Chorwacki"</string>
- <string name="keyboard_layout_czech" msgid="1349256901452975343">"Czeski"</string>
- <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Styl czeskiej klawiatury QWERTY"</string>
- <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoński"</string>
- <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Węgierski"</string>
- <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandzki"</string>
- <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"Brazylijski"</string>
- <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"Portugalski"</string>
- <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Słowacki"</string>
- <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Słoweński"</string>
- <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turecki"</string>
- <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turecka F"</string>
- <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiński"</string>
+ <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"angielski (Wielka Brytania)"</string>
+ <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"angielski (USA)"</string>
+ <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"angielski (USA), międzynarodowy"</string>
+ <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"angielski (USA), Colemak"</string>
+ <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"angielski (USA), Dvorak"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"angielski (USA), Workman"</string>
+ <string name="keyboard_layout_german_label" msgid="8451565865467909999">"niemiecki"</string>
+ <string name="keyboard_layout_french_label" msgid="813450119589383723">"francuski"</string>
+ <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"francuski (Kanada)"</string>
+ <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"rosyjski"</string>
+ <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"rosyjski, Mac"</string>
+ <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"hiszpański"</string>
+ <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"francuski (Szwajcaria)"</string>
+ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"niemiecki (Szwajcaria)"</string>
+ <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijski"</string>
+ <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bułgarski"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bułgarski (znaki fonetyczne)"</string>
+ <string name="keyboard_layout_italian" msgid="6497079660449781213">"włoski"</string>
+ <string name="keyboard_layout_danish" msgid="8036432066627127851">"duński"</string>
+ <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norweski"</string>
+ <string name="keyboard_layout_swedish" msgid="732959109088479351">"szwedzki"</string>
+ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"fiński"</string>
+ <string name="keyboard_layout_croatian" msgid="4172229471079281138">"chorwacki"</string>
+ <string name="keyboard_layout_czech" msgid="1349256901452975343">"czeski"</string>
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"czeski, QWERTY"</string>
+ <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estoński"</string>
+ <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"węgierski"</string>
+ <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandzki"</string>
+ <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"brazylijski"</string>
+ <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"portugalski"</string>
+ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"słowacki"</string>
+ <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"słoweński"</string>
+ <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecki"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turecki F"</string>
+ <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukraiński"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabski"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"grecki"</string>
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrajski"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litewski"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"hiszpański (Ameryka Łacińska)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"łotewski"</string>
- <string name="keyboard_layout_persian" msgid="3920643161015888527">"Perski"</string>
- <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerski"</string>
- <string name="keyboard_layout_polish" msgid="1121588624094925325">"Polski"</string>
- <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Białoruski"</string>
- <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolski"</string>
- <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruziński"</string>
+ <string name="keyboard_layout_persian" msgid="3920643161015888527">"perski"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azerski"</string>
+ <string name="keyboard_layout_polish" msgid="1121588624094925325">"polski"</string>
+ <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"białoruski"</string>
+ <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolski"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruziński"</string>
</resources>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index 806734f..33543fd 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -80,9 +80,9 @@
<string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"Wearలో ఇన్స్టాల్/అన్ఇన్స్టాల్ చర్యలకు మద్దతు లేదు."</string>
<string name="message_staging" msgid="8032722385658438567">"యాప్ను సిద్ధం చేస్తుంది…"</string>
<string name="app_name_unknown" msgid="6881210203354323926">"తెలియదు"</string>
- <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టాబ్లెట్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
- <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టీవీ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
- <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ ఫోన్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
+ <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"మీ సెక్యూరిటీ దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టాబ్లెట్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
+ <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"మీ సెక్యూరిటీ దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టీవీ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
+ <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"మీ సెక్యూరిటీ దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ ఫోన్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
<string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"తెలియని యాప్లు మీ ఫోన్ పైన, వ్యక్తిగత డేటా పైన దాడి చేయడానికి ఎక్కువగా అవకాశం ఉంటుంది. ఈ యాప్ను ఇన్స్టాల్ చేయడం ద్వారా, దాని వినియోగంతో మీ ఫోన్కు ఏదైనా నష్టం జరిగితే లేదా మీ డేటాను కోల్పోతే అందుకు మీరే బాధ్యత వహిస్తారని అంగీకరిస్తున్నారు."</string>
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"మీ టాబ్లెట్ మరియు వ్యక్తిగత డేటాపై తెలియని యాప్లు దాడి చేయడానికి ఎక్కువ అవకాశం ఉంది. మీరు ఈ యాప్ను ఇన్స్టాల్ చేయడం ద్వారా, దీనిని ఉపయోగించడం వలన మీ టాబ్లెట్కు ఏదైనా హాని జరిగినా లేదా డేటా కోల్పోయినా బాధ్యత మీదేనని అంగీకరిస్తున్నారు."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"మీ టీవీ మరియు వ్యక్తిగత డేటాపై తెలియని యాప్లు దాడి చేయడానికి ఎక్కువ అవకాశం ఉంది. మీరు ఈ యాప్ను ఇన్స్టాల్ చేయడం ద్వారా, దీనిని ఉపయోగించడం వలన మీ టీవీకి ఏదైనా హాని జరిగినా లేదా డేటా కోల్పోయినా బాధ్యత మీదేనని అంగీకరిస్తున్నారు."</string>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
index c61b980..44e0318 100644
--- a/packages/PackageInstaller/res/values-zu/strings.xml
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -87,7 +87,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ithebulethi yakho nedatha yomuntu siqu zisengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala kuthebulethi yakho noma ukulahleka kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Idatha yakho ye-TV neyomuntu siqu isengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala ku-TV yakho noma ukulahlekelwa kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Qhubeka"</string>
- <string name="external_sources_settings" msgid="4046964413071713807">"Izilungiselelo"</string>
+ <string name="external_sources_settings" msgid="4046964413071713807">"Amasethingi"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Ifaka/ikhipha izinhlelo zokusebenza ze-wear"</string>
<string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Isaziso sokufakwa kohlelo lokusebenza"</string>
<string name="notification_installation_success_message" msgid="6450467996056038442">"Ifakwe ngempumelelo"</string>
diff --git a/packages/PrintSpooler/res/values-te/strings.xml b/packages/PrintSpooler/res/values-te/strings.xml
index 50e6f3b..a1ed2ca 100644
--- a/packages/PrintSpooler/res/values-te/strings.xml
+++ b/packages/PrintSpooler/res/values-te/strings.xml
@@ -31,8 +31,8 @@
<string name="template_all_pages" msgid="3322235982020148762">"మొత్తం <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> పరిధి"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ఉదా. 1—5,8,11—13"</string>
- <string name="print_preview" msgid="8010217796057763343">"ముద్రణ పరిదృశ్యం"</string>
- <string name="install_for_print_preview" msgid="6366303997385509332">"పరిదృశ్యం చేయడానికి PDF వ్యూయర్ను ఇన్స్టాల్ చేయండి"</string>
+ <string name="print_preview" msgid="8010217796057763343">"ముద్రణ ప్రివ్యూ"</string>
+ <string name="install_for_print_preview" msgid="6366303997385509332">"ప్రివ్యూ చేయడానికి PDF వ్యూయర్ను ఇన్స్టాల్ చేయండి"</string>
<string name="printing_app_crashed" msgid="854477616686566398">"ముద్రణ యాప్ క్రాష్ అయ్యింది"</string>
<string name="generating_print_job" msgid="3119608742651698916">"ముద్రణ జాబ్ను ఉత్పన్నం చేస్తోంది"</string>
<string name="save_as_pdf" msgid="5718454119847596853">"PDF వలె సేవ్ చేయి"</string>
@@ -106,6 +106,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"క్షమించండి, అది పని చేయలేదు. మళ్లీ ప్రయత్నించండి."</string>
<string name="print_error_retry" msgid="1426421728784259538">"మళ్లీ ప్రయత్నించు"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ఈ ప్రింటర్ ప్రస్తుతం అందుబాటులో లేదు."</string>
- <string name="print_cannot_load_page" msgid="6179560924492912009">"పరిదృశ్యాన్ని ప్రదర్శించడం సాధ్యపడలేదు"</string>
- <string name="print_preparing_preview" msgid="3939930735671364712">"పరిదృశ్యం సిద్ధమవుతోంది…"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ప్రివ్యూను ప్రదర్శించడం సాధ్యపడలేదు"</string>
+ <string name="print_preparing_preview" msgid="3939930735671364712">"ప్రివ్యూ సిద్ధమవుతోంది…"</string>
</resources>
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index 36c2bda..7f17d26 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import com.android.settingslib.utils.BuildCompatUtils;
@@ -37,11 +36,10 @@
if (BuildCompatUtils.isAtLeastS()) {
final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
intent.setPackage(PACKAGE_NAME_SETTINGS);
- final ResolveInfo resolveInfo =
- context.getPackageManager().resolveActivity(intent, 0 /* flags */);
- return resolveInfo != null
- && resolveInfo.activityInfo != null
- && resolveInfo.activityInfo.enabled;
+ final boolean isEmbeddingActivityEnabled =
+ intent.resolveActivity(context.getPackageManager()) != null;
+
+ return isEmbeddingActivityEnabled;
}
return false;
}
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
index 2272a37..2624a41 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
@@ -21,10 +21,10 @@
<dimen name="settingslib_switchbar_margin">16dp</dimen>
<!-- Size of layout margin left -->
- <dimen name="settingslib_switchbar_padding_left">24dp</dimen>
+ <dimen name="settingslib_switchbar_padding_left">20dp</dimen>
<!-- Size of layout margin right -->
- <dimen name="settingslib_switchbar_padding_right">16dp</dimen>
+ <dimen name="settingslib_switchbar_padding_right">20dp</dimen>
<!-- Minimum width of switch -->
<dimen name="settingslib_min_switch_width">52dp</dimen>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index 6362882..157a54e 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -24,7 +24,7 @@
<dimen name="settingslib_restricted_icon_margin_end">16dp</dimen>
<!-- Size of title margin -->
- <dimen name="settingslib_switch_title_margin">16dp</dimen>
+ <dimen name="settingslib_switch_title_margin">24dp</dimen>
<!-- SwitchBar sub settings margin start / end -->
<dimen name="settingslib_switchbar_subsettings_margin_start">72dp</dimen>
diff --git a/packages/SettingsLib/SearchWidget/res/values-sv/strings.xml b/packages/SettingsLib/SearchWidget/res/values-sv/strings.xml
index 96e0b4e..0dbc0ea 100644
--- a/packages/SettingsLib/SearchWidget/res/values-sv/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-sv/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Sökinställningar"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Sök i inställningar"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e397035..1fd8941 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -462,7 +462,7 @@
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"جارٍ الشحن لاسلكيًا"</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>
+ <string name="battery_info_status_full" msgid="1339002294876531312">"مشحونة"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"إعدادات يتحكم فيها المشرف"</string>
<string name="disabled" msgid="8017887509554714950">"غير مفعّل"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"مسموح به"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 11e443b..6da26b6 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -582,7 +582,7 @@
<string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"सुरू करा"</string>
<string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"बंद करा"</string>
<string name="carrier_network_change_mode" msgid="4257621815706644026">"वाहक नेटवर्क बदलत आहे"</string>
- <string name="data_connection_3g" msgid="931852552688157407">"३G"</string>
+ <string name="data_connection_3g" msgid="931852552688157407">"3G"</string>
<string name="data_connection_edge" msgid="4625509456544797637">"EDGE"</string>
<string name="data_connection_cdma" msgid="9098161966701934334">"१X"</string>
<string name="data_connection_gprs" msgid="1251945769006770189">"GPRS"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index a210e90..dee6894 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -19,7 +19,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
-import android.app.Dialog;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -85,6 +84,8 @@
@VisibleForTesting
protected Context mContext;
+ private final int mThemeResId;
+ private final boolean mCancelIsNeutral;
@VisibleForTesting
protected TextView mZenAlarmWarning;
@VisibleForTesting
@@ -97,10 +98,20 @@
protected LayoutInflater mLayoutInflater;
public EnableZenModeDialog(Context context) {
- mContext = context;
+ this(context, 0);
}
- public Dialog createDialog() {
+ public EnableZenModeDialog(Context context, int themeResId) {
+ this(context, themeResId, false /* cancelIsNeutral */);
+ }
+
+ public EnableZenModeDialog(Context context, int themeResId, boolean cancelIsNeutral) {
+ mContext = context;
+ mThemeResId = themeResId;
+ mCancelIsNeutral = cancelIsNeutral;
+ }
+
+ public AlertDialog createDialog() {
mNotificationManager = (NotificationManager) mContext.
getSystemService(Context.NOTIFICATION_SERVICE);
mForeverId = Condition.newId(mContext).appendPath("forever").build();
@@ -108,9 +119,8 @@
mUserId = mContext.getUserId();
mAttached = false;
- final AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mContext, mThemeResId)
.setTitle(R.string.zen_mode_settings_turn_on_dialog_title)
- .setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.zen_mode_enable_dialog_turn_on,
new DialogInterface.OnClickListener() {
@Override
@@ -140,6 +150,12 @@
}
});
+ if (mCancelIsNeutral) {
+ builder.setNeutralButton(R.string.cancel, null);
+ } else {
+ builder.setNegativeButton(R.string.cancel, null);
+ }
+
View contentView = getContentView();
bindConditions(forever());
builder.setView(contentView);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index bf0dc7b..1343895 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -261,8 +261,6 @@
private void updateWifiState() {
state = mWifiManager.getWifiState();
enabled = state == WifiManager.WIFI_STATE_ENABLED;
- isCarrierMerged = false;
- subId = 0;
}
private void updateRssi(int newRssi) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 9cd7083..c30c742 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -98,6 +98,10 @@
private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
+ // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
+ // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
+ // restoring this data.
+ private static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
@@ -253,7 +257,7 @@
deviceSpecificInformation, data);
stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
- KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
+ KEY_SIM_SPECIFIC_SETTINGS_2, simSpecificSettingsData, data);
writeNewChecksums(stateChecksums, newState);
}
@@ -395,6 +399,9 @@
break;
case KEY_SIM_SPECIFIC_SETTINGS:
+ // Intentional fall through so that sim-specific backups from Android 12 will
+ // also be restored on newer Android versions.
+ case KEY_SIM_SPECIFIC_SETTINGS_2:
byte[] restoredSimSpecificSettings = new byte[size];
data.readEntityData(restoredSimSpecificSettings, 0, size);
restoreSimSpecificSettings(restoredSimSpecificSettings);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index db301f6..4c9500c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -25,6 +25,7 @@
import static android.provider.Settings.SET_ALL_RESULT_DISABLED;
import static android.provider.Settings.SET_ALL_RESULT_FAILURE;
import static android.provider.Settings.SET_ALL_RESULT_SUCCESS;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
@@ -1337,6 +1338,13 @@
// Anyone can get the global settings, so no security checks.
for (int i = 0; i < nameCount; i++) {
String name = names.get(i);
+ try {
+ enforceSettingReadable(name, SETTINGS_TYPE_GLOBAL,
+ UserHandle.getCallingUserId());
+ } catch (SecurityException e) {
+ // Caller doesn't have permission to read this setting
+ continue;
+ }
Setting setting = settingsState.getSettingLocked(name);
appendSettingToCursor(result, setting);
}
@@ -1514,6 +1522,13 @@
continue;
}
+ try {
+ enforceSettingReadable(name, SETTINGS_TYPE_SECURE, callingUserId);
+ } catch (SecurityException e) {
+ // Caller doesn't have permission to read this setting
+ continue;
+ }
+
// As of Android O, the SSAID is read from an app-specific entry in table
// SETTINGS_FILE_SSAID, unless accessed by a system process.
final Setting setting;
@@ -1776,7 +1791,12 @@
for (int i = 0; i < nameCount; i++) {
String name = names.get(i);
-
+ try {
+ enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, callingUserId);
+ } catch (SecurityException e) {
+ // Caller doesn't have permission to read this setting
+ continue;
+ }
// Determine the owning user as some profile settings are cloned from the parent.
final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId,
name);
@@ -2082,7 +2102,7 @@
}
if ((ai.flags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
// Skip checking readable annotations for test_only apps
- checkReadableAnnotation(settingsType, settingName, ai.targetSandboxVersion);
+ checkReadableAnnotation(settingsType, settingName, ai.targetSdkVersion);
}
/**
* some settings need additional permission check, this is to have a matching security
@@ -3585,7 +3605,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 204;
+ private static final int SETTINGS_VERSION = 205;
private final int mUserId;
@@ -5227,6 +5247,30 @@
currentVersion = 204;
}
+ if (currentVersion == 204) {
+ // Version 204: Reset the
+ // Secure#ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT as enabled
+ // status for showing the tooltips.
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting accessibilityButtonMode = secureSettings.getSettingLocked(
+ Secure.ACCESSIBILITY_BUTTON_MODE);
+ if (!accessibilityButtonMode.isNull()
+ && accessibilityButtonMode.getValue().equals(
+ String.valueOf(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU))) {
+ if (isGestureNavigateEnabled()
+ && hasValueInA11yButtonTargets(secureSettings)) {
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+ /* enabled */ "1",
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
+ currentVersion = 205;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1581e24..6f96a05 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -571,6 +571,9 @@
<!-- Permission required for GTS test - PendingSystemUpdateTest -->
<uses-permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE" />
+ <!-- Permission required for CTS test - SettingsMultiPaneDeepLinkTest -->
+ <uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 462aa57..9a530af 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -121,6 +121,7 @@
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MONITOR_INPUT" />
+ <uses-permission android:name="android.permission.ALLOW_SLIPPERY_TOUCHES" />
<uses-permission android:name="android.permission.INPUT_CONSUMER" />
<!-- DreamManager -->
diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml b/packages/SystemUI/animation/res/anim/launch_dialog_enter.xml
similarity index 100%
rename from packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml
rename to packages/SystemUI/animation/res/anim/launch_dialog_enter.xml
diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml b/packages/SystemUI/animation/res/anim/launch_dialog_exit.xml
similarity index 100%
rename from packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml
rename to packages/SystemUI/animation/res/anim/launch_dialog_exit.xml
diff --git a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_y.xml b/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_y.xml
deleted file mode 100644
index a268abc..0000000
--- a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_y.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1" />
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/values/ids.xml b/packages/SystemUI/animation/res/values/ids.xml
index c4cb89f..ef60a24 100644
--- a/packages/SystemUI/animation/res/values/ids.xml
+++ b/packages/SystemUI/animation/res/values/ids.xml
@@ -16,5 +16,4 @@
-->
<resources>
<item type="id" name="launch_animation_running"/>
- <item type="id" name="dialog_content_parent" />
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/values/styles.xml b/packages/SystemUI/animation/res/values/styles.xml
index ad06c91..3b3f7f6 100644
--- a/packages/SystemUI/animation/res/values/styles.xml
+++ b/packages/SystemUI/animation/res/values/styles.xml
@@ -15,15 +15,10 @@
limitations under the License.
-->
<resources>
- <style name="HostDialogTheme">
- <item name="android:windowAnimationStyle">@style/Animation.HostDialog</item>
- <item name="android:windowIsFloating">false</item>
- <item name="android:backgroundDimEnabled">true</item>
- <item name="android:navigationBarColor">@android:color/transparent</item>
- </style>
-
- <style name="Animation.HostDialog" parent="@android:style/Animation">
- <item name="android:windowEnterAnimation">@anim/launch_host_dialog_enter</item>
- <item name="android:windowExitAnimation">@anim/launch_host_dialog_exit</item>
+ <!-- An animation used by DialogLaunchAnimator to make a dialog appear instantly (to animate -->
+ <!-- in-window) and disappear by fading out (when the exit into view is disabled). -->
+ <style name="Animation.LaunchAnimation" parent="@android:style/Animation">
+ <item name="android:windowEnterAnimation">@anim/launch_dialog_enter</item>
+ <item name="android:windowExitAnimation">@anim/launch_dialog_exit</item>
</style>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 7020603..a0d335d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -21,6 +21,7 @@
import android.app.PendingIntent
import android.app.TaskInfo
import android.graphics.Matrix
+import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
import android.os.Looper
@@ -34,6 +35,7 @@
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.view.animation.Interpolator
import android.view.animation.PathInterpolator
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
@@ -45,16 +47,46 @@
* A class that allows activities to be started in a seamless way from a view that is transforming
* nicely into the starting window.
*/
-class ActivityLaunchAnimator(private val launchAnimator: LaunchAnimator) {
+class ActivityLaunchAnimator(
+ private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS)
+) {
companion object {
+ @JvmField
+ val TIMINGS = LaunchAnimator.Timings(
+ totalDuration = 500L,
+ contentBeforeFadeOutDelay = 0L,
+ contentBeforeFadeOutDuration = 150L,
+ contentAfterFadeInDelay = 150L,
+ contentAfterFadeInDuration = 183L
+ )
+
+ val INTERPOLATORS = LaunchAnimator.Interpolators(
+ positionInterpolator = Interpolators.EMPHASIZED,
+ positionXInterpolator = createPositionXInterpolator(),
+ contentBeforeFadeOutInterpolator = Interpolators.LINEAR_OUT_SLOW_IN,
+ contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f)
+ )
+
+ /** Durations & interpolators for the navigation bar fading in & out. */
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
- private const val ANIMATION_DELAY_NAV_FADE_IN =
- LaunchAnimator.ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
+ private val ANIMATION_DELAY_NAV_FADE_IN =
+ TIMINGS.totalDuration - ANIMATION_DURATION_NAV_FADE_IN
+
+ private val NAV_FADE_IN_INTERPOLATOR = Interpolators.STANDARD_DECELERATE
+ private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
+
+ /** The time we wait before timing out the remote animation after starting the intent. */
private const val LAUNCH_TIMEOUT = 1000L
- private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
- private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
+ private fun createPositionXInterpolator(): Interpolator {
+ val path = Path().apply {
+ moveTo(0f, 0f)
+ cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f)
+ cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f)
+ }
+ return PathInterpolator(path)
+ }
}
/**
@@ -107,8 +139,8 @@
val animationAdapter = if (!hideKeyguardWithAnimation) {
RemoteAnimationAdapter(
runner,
- LaunchAnimator.ANIMATION_DURATION,
- LaunchAnimator.ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
+ TIMINGS.totalDuration,
+ TIMINGS.totalDuration - 150 /* statusBarTransitionDelay */
)
} else {
null
@@ -436,7 +468,6 @@
.withAlpha(1f)
.withMatrix(matrix)
.withWindowCrop(windowCrop)
- .withLayer(window.prefixOrderIndex)
.withCornerRadius(cornerRadius)
.withVisibility(true)
.build()
@@ -449,7 +480,7 @@
state: LaunchAnimator.State,
linearProgress: Float
) {
- val fadeInProgress = LaunchAnimator.getProgress(linearProgress,
+ val fadeInProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress,
ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT)
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
@@ -464,7 +495,7 @@
.withWindowCrop(windowCrop)
.withVisibility(true)
} else {
- val fadeOutProgress = LaunchAnimator.getProgress(linearProgress, 0,
+ val fadeOutProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress, 0,
ANIMATION_DURATION_NAV_FADE_OUT)
params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress))
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 9aad278..f7a7603 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -20,38 +20,42 @@
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.Dialog
-import android.content.Context
import android.graphics.Color
import android.graphics.Rect
import android.os.Looper
+import android.service.dreams.IDreamManager
import android.util.Log
import android.util.MathUtils
import android.view.GhostView
-import android.view.Gravity
+import android.view.SurfaceControl
import android.view.View
import android.view.ViewGroup
-import android.view.ViewTreeObserver.OnPreDrawListener
-import android.view.WindowInsets
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewRootImpl
import android.view.WindowManager
-import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-import android.view.WindowManagerPolicyConstants
import android.widget.FrameLayout
import kotlin.math.roundToInt
private const val TAG = "DialogLaunchAnimator"
-private val DIALOG_CONTENT_PARENT_ID = R.id.dialog_content_parent
/**
* A class that allows dialogs to be started in a seamless way from a view that is transforming
* nicely into the starting dialog.
*/
-class DialogLaunchAnimator(
- private val context: Context,
- private val launchAnimator: LaunchAnimator,
- private val hostDialogProvider: HostDialogProvider
+class DialogLaunchAnimator @JvmOverloads constructor(
+ private val dreamManager: IDreamManager,
+ private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
+ private var isForTesting: Boolean = false
) {
private companion object {
+ private val TIMINGS = ActivityLaunchAnimator.TIMINGS
+
+ // We use the same interpolator for X and Y axis to make sure the dialog does not move out
+ // of the screen bounds during the animation.
+ private val INTERPOLATORS = ActivityLaunchAnimator.INTERPOLATORS.copy(
+ positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator
+ )
+
private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.launch_animation_running
}
@@ -63,123 +67,75 @@
private val openedDialogs = hashSetOf<AnimatedDialog>()
/**
- * Show [dialog] by expanding it from [view]. If [animateBackgroundBoundsChange] is true, then
- * the background of the dialog will be animated when the dialog bounds change.
+ * Show [dialog] by expanding it from [view]. If [view] is a view inside another dialog that was
+ * shown using this method, then we will animate from that dialog instead.
*
- * Caveats: When calling this function, the dialog content view will actually be stolen and
- * attached to a different dialog (and thus a different window) which means that the actual
- * dialog window will never be drawn. Moreover, unless [dialog] is a [ListenableDialog], you
- * must call dismiss(), hide() and show() on the [Dialog] returned by this function to actually
- * dismiss, hide or show the dialog.
+ * If [animateBackgroundBoundsChange] is true, then the background of the dialog will be
+ * animated when the dialog bounds change.
+ *
+ * Caveats: When calling this function and [dialog] is not a fullscreen dialog, then it will be
+ * made fullscreen and 2 views will be inserted between the dialog DecorView and its children.
*/
@JvmOverloads
fun showFromView(
dialog: Dialog,
view: View,
animateBackgroundBoundsChange: Boolean = false
- ): Dialog {
+ ) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw IllegalStateException(
"showFromView must be called from the main thread and dialog must be created in " +
"the main thread")
}
- // If the parent of the view we are launching from is the background of some other animated
- // dialog, then this means the caller intent is to launch a dialog from another dialog. In
- // this case, we also animate the parent (which is the dialog background).
+ // If the view we are launching from belongs to another dialog, then this means the caller
+ // intent is to launch a dialog from another dialog.
val animatedParent = openedDialogs
- .firstOrNull { it.dialogContentParent == view.parent }
- val parentHostDialog = animatedParent?.hostDialog
- val animateFrom = animatedParent?.dialogContentParent ?: view
+ .firstOrNull { it.dialog.window.decorView.viewRootImpl == view.viewRootImpl }
+ val animateFrom = animatedParent?.dialogContentWithBackground ?: view
// Make sure we don't run the launch animation from the same view twice at the same time.
if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
Log.e(TAG, "Not running dialog launch animation as there is already one running")
dialog.show()
- return dialog
+ return
}
animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
val animatedDialog = AnimatedDialog(
- context,
launchAnimator,
- hostDialogProvider,
+ dreamManager,
animateFrom,
onDialogDismissed = { openedDialogs.remove(it) },
- originalDialog = dialog,
+ dialog = dialog,
animateBackgroundBoundsChange,
- openedDialogs.firstOrNull { it.hostDialog == parentHostDialog }
+ animatedParent,
+ isForTesting
)
- val hostDialog = animatedDialog.hostDialog
+
openedDialogs.add(animatedDialog)
-
- // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the
- // host dialog.
- if (dialog is ListenableDialog) {
- dialog.addListener(object : DialogListener {
- override fun onDismiss(reason: DialogListener.DismissReason) {
- dialog.removeListener(this)
-
- // We disable the exit animation if we are dismissing the dialog because the
- // device is being locked, otherwise the animation looks bad if AOD is enabled.
- // If AOD is disabled the screen will directly becomes black and we won't see
- // the animation anyways.
- if (reason == DialogListener.DismissReason.DEVICE_LOCKED) {
- animatedDialog.exitAnimationDisabled = true
- }
-
- hostDialog.dismiss()
- }
-
- override fun onHide() {
- if (animatedDialog.ignoreNextCallToHide) {
- animatedDialog.ignoreNextCallToHide = false
- return
- }
-
- hostDialog.hide()
- }
-
- override fun onShow() {
- hostDialog.show()
-
- // We don't actually want to show the original dialog, so hide it.
- animatedDialog.ignoreNextCallToHide = true
- dialog.hide()
- }
-
- override fun onSizeChanged() {
- animatedDialog.onOriginalDialogSizeChanged()
- }
-
- override fun prepareForStackDismiss() {
- animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss()
- }
- })
- }
-
animatedDialog.start()
- return hostDialog
}
/**
- * Launch [dialog] from a [parentHostDialog] as returned by [showFromView]. This will allow
- * for dismissing the whole stack.
+ * Launch [dialog] from [another dialog][animateFrom] that was shown using [showFromView]. This
+ * will allow for dismissing the whole stack.
*
- * This will return a new host dialog, with the same caveat as [showFromView].
- *
- * @see DialogListener.prepareForStackDismiss
+ * @see dismissStack
*/
fun showFromDialog(
dialog: Dialog,
- parentHostDialog: Dialog,
+ animateFrom: Dialog,
animateBackgroundBoundsChange: Boolean = false
- ): Dialog {
- val view = parentHostDialog.findViewById<ViewGroup>(DIALOG_CONTENT_PARENT_ID)
- ?.getChildAt(0)
- ?: throw IllegalStateException("No dialog content parent found in host dialog")
- return showFromView(dialog, view, animateBackgroundBoundsChange)
+ ) {
+ val view = openedDialogs
+ .firstOrNull { it.dialog == animateFrom }
+ ?.dialogContentWithBackground
+ ?: throw IllegalStateException(
+ "The animateFrom dialog was not animated using " +
+ "DialogLaunchAnimator.showFrom(View|Dialog)")
+ showFromView(dialog, view, animateBackgroundBoundsChange)
}
/**
@@ -195,69 +151,22 @@
fun disableAllCurrentDialogsExitAnimations() {
openedDialogs.forEach { it.exitAnimationDisabled = true }
}
-}
-interface HostDialogProvider {
/**
- * Create a host dialog that will be used to host a launch animation. This host dialog must:
- * 1. call [onCreateCallback] in its onCreate() method, e.g. right after calling
- * super.onCreate().
- * 2. call [dismissOverride] instead of doing any dismissing logic. The actual dismissing
- * logic should instead be done inside the lambda passed to [dismissOverride], which will
- * be called after the exit animation.
- * 3. Be full screen, i.e. have a window matching its parent size.
- *
- * See SystemUIHostDialogProvider for an example of implementation.
+ * Dismiss [dialog]. If it was launched from another dialog using [showFromView], also dismiss
+ * the stack of dialogs, animating back to the original touchSurface.
*/
- fun createHostDialog(
- context: Context,
- theme: Int,
- onCreateCallback: () -> Unit,
- dismissOverride: (() -> Unit) -> Unit
- ): Dialog
-}
-
-/** A dialog to/from which we can add/remove listeners. */
-interface ListenableDialog {
- /** Add [listener] to the listeners. */
- fun addListener(listener: DialogListener)
-
- /** Remove [listener] from the listeners. */
- fun removeListener(listener: DialogListener)
-}
-
-interface DialogListener {
- /** The reason why a dialog was dismissed. */
- enum class DismissReason {
- UNKNOWN,
-
- /** The device was locked, which dismissed this dialog. */
- DEVICE_LOCKED,
+ fun dismissStack(dialog: Dialog) {
+ openedDialogs
+ .firstOrNull { it.dialog == dialog }
+ ?.let { it.touchSurface = it.prepareForStackDismiss() }
+ dialog.dismiss()
}
-
- /** Called when this dialog dismiss() is called. */
- fun onDismiss(reason: DismissReason)
-
- /** Called when this dialog hide() is called. */
- fun onHide()
-
- /** Called when this dialog show() is called. */
- fun onShow()
-
- /**
- * Call before dismissing a stack of dialogs (dialogs launched from dialogs), so the topmost
- * can animate directly into the original `touchSurface`.
- */
- fun prepareForStackDismiss()
-
- /** Called when this dialog size might have changed, e.g. because of configuration changes. */
- fun onSizeChanged()
}
private class AnimatedDialog(
- private val context: Context,
private val launchAnimator: LaunchAnimator,
- hostDialogProvider: HostDialogProvider,
+ private val dreamManager: IDreamManager,
/** The view that triggered the dialog after being tapped. */
var touchSurface: View,
@@ -268,37 +177,39 @@
*/
private val onDialogDismissed: (AnimatedDialog) -> Unit,
- /** The original dialog whose content will be shown and animate in/out in [hostDialog]. */
- private val originalDialog: Dialog,
+ /** The dialog to show and animate. */
+ val dialog: Dialog,
/** Whether we should animate the dialog background when its bounds change. */
- private val animateBackgroundBoundsChange: Boolean,
+ animateBackgroundBoundsChange: Boolean,
- /** Launch animation corresponding to the parent [hostDialog]. */
- private val parentAnimatedDialog: AnimatedDialog? = null
+ /** Launch animation corresponding to the parent [AnimatedDialog]. */
+ private val parentAnimatedDialog: AnimatedDialog? = null,
+
+ /**
+ * Whether we are currently running in a test, in which case we need to disable
+ * synchronization.
+ */
+ private val isForTesting: Boolean
) {
/**
- * The fullscreen dialog to which we will add the content view [originalDialogView] of
- * [originalDialog].
- */
- val hostDialog = hostDialogProvider.createHostDialog(
- context, R.style.HostDialogTheme, this::onHostDialogCreated, this::onHostDialogDismissed)
-
- /** The root content view of [hostDialog]. */
- private val hostDialogRoot = FrameLayout(context)
+ * The DecorView of this dialog window.
+ *
+ * Note that we access this DecorView lazily to avoid accessing it before the dialog is created,
+ * which can sometimes cause crashes (e.g. with the Cast dialog).
+ */
+ private val decorView by lazy { dialog.window!!.decorView as ViewGroup }
/**
- * The parent of the original dialog content view, that serves as a fake window that will have
- * the same size as the original dialog window and to which we will set the original dialog
- * window background.
+ * The dialog content with its background. When animating a fullscreen dialog, this is just the
+ * first ViewGroup of the dialog that has a background. When animating a normal (not fullscreen)
+ * dialog, this is an additional view that serves as a fake window that will have the same size
+ * as the dialog window initially had and to which we will set the dialog window background.
*/
- val dialogContentParent = FrameLayout(context).apply {
- id = DIALOG_CONTENT_PARENT_ID
- }
+ var dialogContentWithBackground: ViewGroup? = null
/**
- * The background color of [originalDialogView], taking into consideration the [originalDialog]
- * window background color.
+ * The background color of [dialog], taking into consideration its window background color.
*/
private var originalDialogBackgroundColor = Color.BLACK
@@ -311,201 +222,145 @@
private var isDismissing = false
private var dismissRequested = false
- var ignoreNextCallToHide = false
var exitAnimationDisabled = false
private var isTouchSurfaceGhostDrawn = false
private var isOriginalDialogViewLaidOut = false
- private var backgroundLayoutListener = if (animateBackgroundBoundsChange) {
+
+ /** A layout listener to animate the dialog height change. */
+ private val backgroundLayoutListener = if (animateBackgroundBoundsChange) {
AnimatedBoundsLayoutListener()
} else {
null
}
+ /*
+ * A layout listener in case the dialog (window) size changes (for instance because of a
+ * configuration change) to ensure that the dialog stays full width.
+ */
+ private var decorViewLayoutListener: View.OnLayoutChangeListener? = null
+
fun start() {
- // Show the host (fullscreen) dialog, to which we will add the stolen dialog view.
- hostDialog.show()
+ // Create the dialog so that its onCreate() method is called, which usually sets the dialog
+ // content.
+ dialog.create()
- // Steal the dialog view. We do that by showing it but preventing it from drawing, then
- // hiding it as soon as its content is available.
- stealOriginalDialogContentView(then = this::showDialogFromView)
- }
-
- private fun onHostDialogCreated() {
- // Make the dialog fullscreen with a transparent background.
- hostDialog.setContentView(
- hostDialogRoot,
- ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- )
- )
-
- val window = hostDialog.window
- ?: throw IllegalStateException("There is no window associated to the host dialog")
- window.setBackgroundDrawableResource(android.R.color.transparent)
-
- // If we are using gesture navigation, then we can overlay the navigation/task bars with
- // the host dialog.
- val navigationMode = context.resources.getInteger(
- com.android.internal.R.integer.config_navBarInteractionMode)
- if (navigationMode == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL) {
- window.attributes.fitInsetsTypes = window.attributes.fitInsetsTypes and
- WindowInsets.Type.navigationBars().inv()
- window.addFlags(FLAG_LAYOUT_IN_SCREEN or FLAG_LAYOUT_INSET_DECOR)
- window.setDecorFitsSystemWindows(false)
- }
-
- // Disable the dim. We will enable it once we start the animation.
- window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
-
- // Add a temporary touch surface ghost as soon as the window is ready to draw. This
- // temporary ghost will be drawn together with the touch surface, but in the host dialog
- // window. Once it is drawn, we will make the touch surface invisible, and then start the
- // animation. We do all this synchronization to avoid flicker that would occur if we made
- // the touch surface invisible too early (before its ghost is drawn), leading to one or more
- // frames with a hole instead of the touch surface (or its ghost).
- hostDialogRoot.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this)
- addTemporaryTouchSurfaceGhost()
- return true
- }
- })
- hostDialogRoot.invalidate()
- }
-
- private fun addTemporaryTouchSurfaceGhost() {
- // Create a ghost of the touch surface (which will make the touch surface invisible) and add
- // it to the host dialog. We will wait for this ghost to be drawn before starting the
- // animation.
- val ghost = GhostView.addGhost(touchSurface, hostDialogRoot)
-
- // The ghost of the touch surface was just created, so the touch surface was made invisible.
- // We make it visible again until the ghost is actually drawn.
- touchSurface.visibility = View.VISIBLE
-
- // Wait for the ghost to be drawn before continuing.
- ghost.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- ghost.viewTreeObserver.removeOnPreDrawListener(this)
- onTouchSurfaceGhostDrawn()
- return true
- }
- })
- ghost.invalidate()
- }
-
- private fun onTouchSurfaceGhostDrawn() {
- // Make the touch surface invisible and make sure that it stays invisible as long as the
- // dialog is shown or animating.
- touchSurface.visibility = View.INVISIBLE
- (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
-
- // Add a pre draw listener to (maybe) start the animation once the touch surface is
- // actually invisible.
- touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- touchSurface.viewTreeObserver.removeOnPreDrawListener(this)
- isTouchSurfaceGhostDrawn = true
- maybeStartLaunchAnimation()
- return true
- }
- })
- touchSurface.invalidate()
- }
-
- /** Get the content view of [originalDialog] and pass it to [then]. */
- private fun stealOriginalDialogContentView(then: (View) -> Unit) {
- // The original dialog content view will be attached to android.R.id.content when the dialog
- // is shown, so we show the dialog and add an observer to get the view but also prevents the
- // original dialog from being drawn.
- val androidContent = originalDialog.findViewById<ViewGroup>(android.R.id.content)
- ?: throw IllegalStateException("Dialog does not have any android.R.id.content view")
-
- androidContent.viewTreeObserver.addOnPreDrawListener(
- object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- if (androidContent.childCount == 1) {
- androidContent.viewTreeObserver.removeOnPreDrawListener(this)
-
- // Hide the animated dialog. Because of the dialog listener set up
- // earlier, this would also hide the host dialog, but in this case we
- // need to keep the host dialog visible.
- ignoreNextCallToHide = true
- originalDialog.hide()
-
- then(androidContent.getChildAt(0))
- return false
- }
-
- // Never draw the original dialog content.
- return false
+ val window = dialog.window!!
+ val isWindowFullScreen =
+ window.attributes.width == MATCH_PARENT && window.attributes.height == MATCH_PARENT
+ val dialogContentWithBackground = if (isWindowFullScreen) {
+ // If the dialog window is already fullscreen, then we look for the first ViewGroup that
+ // has a background (and is not the DecorView, which always has a background) and
+ // animate towards that ViewGroup given that this is probably what represents the actual
+ // dialog view.
+ var viewGroupWithBackground: ViewGroup? = null
+ for (i in 0 until decorView.childCount) {
+ viewGroupWithBackground = findFirstViewGroupWithBackground(decorView.getChildAt(i))
+ if (viewGroupWithBackground != null) {
+ break
}
- })
- originalDialog.show()
- }
+ }
- private fun showDialogFromView(dialogView: View) {
- // Close the dialog when clicking outside of it.
- hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
- dialogView.isClickable = true
-
- // Set the background of the window dialog to the dialog itself.
- // TODO(b/193634619): Support dialog windows without background.
- // TODO(b/193634619): Support dialog whose background comes from the content view instead of
- // the window.
- val typedArray =
- originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window)
- val backgroundRes =
- typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0)
- typedArray.recycle()
- if (backgroundRes == 0) {
- throw IllegalStateException("Dialogs with no backgrounds on window are not supported")
- }
-
- // Add a parent view to the original dialog view to which we will set the original dialog
- // window background. This View serves as a fake window with background, so that we are sure
- // that we don't override the dialog view paddings with the window background that usually
- // has insets.
- dialogContentParent.setBackgroundResource(backgroundRes)
- hostDialogRoot.addView(
- dialogContentParent,
-
- // We give it the size of its original dialog window.
- FrameLayout.LayoutParams(
- originalDialog.window.attributes.width,
- originalDialog.window.attributes.height,
- Gravity.CENTER
+ // Animate that view with the background. Throw if we didn't find one, because otherwise
+ // it's not clear what we should animate.
+ viewGroupWithBackground
+ ?: throw IllegalStateException("Unable to find ViewGroup with background")
+ } else {
+ // We will make the dialog window (and therefore its DecorView) fullscreen to make it
+ // possible to animate outside its bounds.
+ //
+ // Before that, we add a new View as a child of the DecorView with the same size and
+ // gravity as that DecorView, then we add all original children of the DecorView to that
+ // new View. Finally we remove the background of the DecorView and add it to the new
+ // View, then we make the DecorView fullscreen. This new View now acts as a fake (non
+ // fullscreen) window.
+ //
+ // On top of that, we also add a fullscreen transparent background between the DecorView
+ // and the view that we added so that we can dismiss the dialog when this view is
+ // clicked. This is necessary because DecorView overrides onTouchEvent and therefore we
+ // can't set the click listener directly on the (now fullscreen) DecorView.
+ val fullscreenTransparentBackground = FrameLayout(dialog.context)
+ decorView.addView(
+ fullscreenTransparentBackground,
+ 0 /* index */,
+ FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
)
- )
- // Make the dialog view parent invisible for now, to make sure it's not drawn yet.
- dialogContentParent.visibility = View.INVISIBLE
+ val dialogContentWithBackground = FrameLayout(dialog.context)
+ dialogContentWithBackground.background = decorView.background
- val background = dialogContentParent.background!!
+ // Make the window background transparent. Note that setting the window (or DecorView)
+ // background drawable to null leads to issues with background color (not being
+ // transparent) or with insets that are not refreshed. Therefore we need to set it to
+ // something not null, hence we are using android.R.color.transparent here.
+ window.setBackgroundDrawableResource(android.R.color.transparent)
+
+ // Close the dialog when clicking outside of it.
+ fullscreenTransparentBackground.setOnClickListener { dialog.dismiss() }
+ dialogContentWithBackground.isClickable = true
+
+ // Make sure the transparent and dialog backgrounds are not focusable by accessibility
+ // features.
+ fullscreenTransparentBackground.importantForAccessibility =
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ dialogContentWithBackground.importantForAccessibility =
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ fullscreenTransparentBackground.addView(
+ dialogContentWithBackground,
+ FrameLayout.LayoutParams(
+ window.attributes.width,
+ window.attributes.height,
+ window.attributes.gravity
+ )
+ )
+
+ // Move all original children of the DecorView to the new View we just added.
+ for (i in 1 until decorView.childCount) {
+ val view = decorView.getChildAt(1)
+ decorView.removeViewAt(1)
+ dialogContentWithBackground.addView(view)
+ }
+
+ // Make the window fullscreen and add a layout listener to ensure it stays fullscreen.
+ window.setLayout(MATCH_PARENT, MATCH_PARENT)
+ decorViewLayoutListener = View.OnLayoutChangeListener {
+ v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
+ if (window.attributes.width != MATCH_PARENT ||
+ window.attributes.height != MATCH_PARENT) {
+ // The dialog size changed, copy its size to dialogContentWithBackground and
+ // make the dialog window full screen again.
+ val layoutParams = dialogContentWithBackground.layoutParams
+ layoutParams.width = window.attributes.width
+ layoutParams.height = window.attributes.height
+ dialogContentWithBackground.layoutParams = layoutParams
+ window.setLayout(MATCH_PARENT, MATCH_PARENT)
+ }
+ }
+ decorView.addOnLayoutChangeListener(decorViewLayoutListener)
+
+ dialogContentWithBackground
+ }
+ this.dialogContentWithBackground = dialogContentWithBackground
+
+ val background = dialogContentWithBackground.background
originalDialogBackgroundColor =
GhostedViewLaunchAnimatorController.findGradientDrawable(background)
?.color
?.defaultColor ?: Color.BLACK
- // Add the dialog view to its parent (that has the original window background).
- (dialogView.parent as? ViewGroup)?.removeView(dialogView)
- dialogContentParent.addView(
- dialogView,
+ // Make the background view invisible until we start the animation. We use the transition
+ // visibility like GhostView does so that we don't mess up with the accessibility tree (see
+ // b/204944038#comment17).
+ dialogContentWithBackground.setTransitionVisibility(View.INVISIBLE)
- // It should match its parent size, which is sized the same as the original dialog
- // window.
- FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- )
- )
+ // Make sure the dialog is visible instantly and does not do any window animation.
+ window.attributes.windowAnimations = R.style.Animation_LaunchAnimation
- // Start the animation when the dialog is laid out in the center of the host dialog.
- dialogContentParent.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ // Start the animation once the background view is properly laid out.
+ dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(
- view: View,
+ v: View,
left: Int,
top: Int,
right: Int,
@@ -515,25 +370,113 @@
oldRight: Int,
oldBottom: Int
) {
- dialogContentParent.removeOnLayoutChangeListener(this)
+ dialogContentWithBackground.removeOnLayoutChangeListener(this)
isOriginalDialogViewLaidOut = true
maybeStartLaunchAnimation()
}
})
+
+ // Disable the dim. We will enable it once we start the animation.
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+
+ // Override the dialog dismiss() so that we can animate the exit before actually dismissing
+ // the dialog.
+ dialog.setDismissOverride(this::onDialogDismissed)
+
+ // Show the dialog.
+ dialog.show()
+
+ addTouchSurfaceGhost()
}
- fun onOriginalDialogSizeChanged() {
- // The dialog is the single child of the root.
- if (hostDialogRoot.childCount != 1) {
+ private fun addTouchSurfaceGhost() {
+ if (decorView.viewRootImpl == null) {
+ // Make sure that we have access to the dialog view root to synchronize the creation of
+ // the ghost.
+ decorView.post(::addTouchSurfaceGhost)
return
}
- val dialogView = hostDialogRoot.getChildAt(0)
- val layoutParams = dialogView.layoutParams as? FrameLayout.LayoutParams ?: return
- layoutParams.width = originalDialog.window.attributes.width
- layoutParams.height = originalDialog.window.attributes.height
- dialogView.layoutParams = layoutParams
+ // Create a ghost of the touch surface (which will make the touch surface invisible) and add
+ // it to the host dialog. We trigger a one off synchronization to make sure that this is
+ // done in sync between the two different windows.
+ synchronizeNextDraw(then = {
+ isTouchSurfaceGhostDrawn = true
+ maybeStartLaunchAnimation()
+ })
+ GhostView.addGhost(touchSurface, decorView)
+
+ // The ghost of the touch surface was just created, so the touch surface is currently
+ // invisible. We need to make sure that it stays invisible as long as the dialog is shown or
+ // animating.
+ (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
+ }
+
+ /**
+ * Synchronize the next draw of the touch surface and dialog view roots so that they are
+ * performed at the same time, in the same transaction. This is necessary to make sure that the
+ * ghost of the touch surface is drawn at the same time as the touch surface is made invisible
+ * (or inversely, removed from the UI when the touch surface is made visible).
+ */
+ private fun synchronizeNextDraw(then: () -> Unit) {
+ if (isForTesting || !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null ||
+ !decorView.isAttachedToWindow || decorView.viewRootImpl == null) {
+ // No need to synchronize if either the touch surface or dialog view is not attached
+ // to a window.
+ then()
+ return
+ }
+
+ // Consume the next frames of both view roots to make sure the ghost view is drawn at
+ // exactly the same time as when the touch surface is made invisible.
+ var remainingTransactions = 0
+ val mergedTransactions = SurfaceControl.Transaction()
+
+ fun onTransaction(transaction: SurfaceControl.Transaction?) {
+ remainingTransactions--
+ transaction?.let { mergedTransactions.merge(it) }
+
+ if (remainingTransactions == 0) {
+ mergedTransactions.apply()
+ then()
+ }
+ }
+
+ fun consumeNextDraw(viewRootImpl: ViewRootImpl) {
+ if (viewRootImpl.consumeNextDraw(::onTransaction)) {
+ remainingTransactions++
+
+ // Make sure we trigger a traversal.
+ viewRootImpl.view.invalidate()
+ }
+ }
+
+ consumeNextDraw(touchSurface.viewRootImpl)
+ consumeNextDraw(decorView.viewRootImpl)
+
+ if (remainingTransactions == 0) {
+ then()
+ }
+ }
+
+ private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
+ if (view !is ViewGroup) {
+ return null
+ }
+
+ if (view.background != null) {
+ return view
+ }
+
+ for (i in 0 until view.childCount) {
+ val match = findFirstViewGroupWithBackground(view.getChildAt(i))
+ if (match != null) {
+ return match
+ }
+ }
+
+ return null
}
private fun maybeStartLaunchAnimation() {
@@ -542,7 +485,7 @@
}
// Show the background dim.
- hostDialog.window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ dialog.window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
startAnimation(
isLaunching = true,
@@ -564,22 +507,23 @@
// dismiss was called during the animation, dismiss again now to actually
// dismiss.
if (dismissRequested) {
- hostDialog.dismiss()
+ dialog.dismiss()
}
// If necessary, we animate the dialog background when its bounds change. We do it
// at the end of the launch animation, because the lauch animation already correctly
// handles bounds changes.
if (backgroundLayoutListener != null) {
- dialogContentParent.addOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground!!
+ .addOnLayoutChangeListener(backgroundLayoutListener)
}
}
)
}
- private fun onHostDialogDismissed(actualDismiss: () -> Unit) {
+ private fun onDialogDismissed() {
if (Looper.myLooper() != Looper.getMainLooper()) {
- context.mainExecutor.execute { onHostDialogDismissed(actualDismiss) }
+ dialog.context.mainExecutor.execute { onDialogDismissed() }
return
}
@@ -594,23 +538,29 @@
}
isDismissing = true
- hideDialogIntoView { instantDismiss: Boolean ->
- if (instantDismiss) {
- originalDialog.hide()
- hostDialog.hide()
+ hideDialogIntoView { animationRan: Boolean ->
+ if (animationRan) {
+ // Instantly dismiss the dialog if we ran the animation into view. If it was
+ // skipped, dismiss() will run the window animation (which fades out the dialog).
+ dialog.hide()
}
- originalDialog.dismiss()
- actualDismiss()
+ dialog.setDismissOverride(null)
+ dialog.dismiss()
}
}
/**
- * Hide the dialog into the touch surface and call [dismissDialogs] when the animation is done
- * (passing instantDismiss=true) or if it's skipped (passing instantDismiss=false) to actually
- * dismiss the dialogs.
+ * Hide the dialog into the touch surface and call [onAnimationFinished] when the animation is
+ * done (passing animationRan=true) or if it's skipped (passing animationRan=false) to actually
+ * dismiss the dialog.
*/
- private fun hideDialogIntoView(dismissDialogs: (Boolean) -> Unit) {
+ private fun hideDialogIntoView(onAnimationFinished: (Boolean) -> Unit) {
+ // Remove the layout change listener we have added to the DecorView earlier.
+ if (decorViewLayoutListener != null) {
+ decorView.removeOnLayoutChangeListener(decorViewLayoutListener)
+ }
+
if (!shouldAnimateDialogIntoView()) {
Log.i(TAG, "Skipping animation of dialog into the touch surface")
@@ -622,7 +572,7 @@
touchSurface.visibility = View.VISIBLE
}
- dismissDialogs(false /* instantDismiss */)
+ onAnimationFinished(false /* instantDismiss */)
onDialogDismissed(this@AnimatedDialog)
return
}
@@ -631,38 +581,27 @@
isLaunching = false,
onLaunchAnimationStart = {
// Remove the dim background as soon as we start the animation.
- hostDialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ dialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
},
onLaunchAnimationEnd = {
// Make sure we allow the touch surface to change its visibility again.
(touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
touchSurface.visibility = View.VISIBLE
- dialogContentParent.visibility = View.INVISIBLE
+ val dialogContentWithBackground = this.dialogContentWithBackground!!
+ dialogContentWithBackground.visibility = View.INVISIBLE
if (backgroundLayoutListener != null) {
- dialogContentParent.removeOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground
+ .removeOnLayoutChangeListener(backgroundLayoutListener)
}
- // The animated ghost was just removed. We create a temporary ghost that will be
- // removed only once we draw the touch surface, to avoid flickering that would
- // happen when removing the ghost too early (before the touch surface is drawn).
- GhostView.addGhost(touchSurface, hostDialogRoot)
-
- touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- touchSurface.viewTreeObserver.removeOnPreDrawListener(this)
-
- // Now that the touch surface was drawn, we can remove the temporary ghost
- // and instantly dismiss the dialog.
- GhostView.removeGhost(touchSurface)
- dismissDialogs(true /* instantDismiss */)
- onDialogDismissed(this@AnimatedDialog)
-
- return true
- }
+ // Make sure that the removal of the ghost and making the touch surface visible is
+ // done at the same time.
+ synchronizeNextDraw(then = {
+ onAnimationFinished(true /* instantDismiss */)
+ onDialogDismissed(this@AnimatedDialog)
})
- touchSurface.invalidate()
}
)
}
@@ -672,14 +611,14 @@
onLaunchAnimationStart: () -> Unit = {},
onLaunchAnimationEnd: () -> Unit = {}
) {
- // Create 2 ghost controllers to animate both the dialog and the touch surface in the host
+ // Create 2 ghost controllers to animate both the dialog and the touch surface in the
// dialog.
- val startView = if (isLaunching) touchSurface else dialogContentParent
- val endView = if (isLaunching) dialogContentParent else touchSurface
+ val startView = if (isLaunching) touchSurface else dialogContentWithBackground!!
+ val endView = if (isLaunching) dialogContentWithBackground!! else touchSurface
val startViewController = GhostedViewLaunchAnimatorController(startView)
val endViewController = GhostedViewLaunchAnimatorController(endView)
- startViewController.launchContainer = hostDialogRoot
- endViewController.launchContainer = hostDialogRoot
+ startViewController.launchContainer = decorView
+ endViewController.launchContainer = decorView
val endState = endViewController.createAnimatorState()
val controller = object : LaunchAnimator.Controller {
@@ -736,7 +675,15 @@
}
private fun shouldAnimateDialogIntoView(): Boolean {
- if (exitAnimationDisabled) {
+ // Don't animate if the dialog was previously hidden using hide() or if we disabled the exit
+ // animation.
+ if (exitAnimationDisabled || !dialog.isShowing) {
+ return false
+ }
+
+ // If we are dreaming, the dialog was probably closed because of that so we don't animate
+ // into the touchSurface.
+ if (dreamManager.isDreaming) {
return false
}
@@ -837,9 +784,9 @@
return touchSurface
}
parentAnimatedDialog.exitAnimationDisabled = true
- parentAnimatedDialog.originalDialog.hide()
+ parentAnimatedDialog.dialog.hide()
val view = parentAnimatedDialog.prepareForStackDismiss()
- parentAnimatedDialog.originalDialog.dismiss()
+ parentAnimatedDialog.dialog.dismiss()
// Make the touch surface invisible, so we end up animating to it when we actually
// dismiss the stack
view.visibility = View.INVISIBLE
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 3ccf5e4..86cd357 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -186,7 +186,11 @@
// Making the ghost view invisible will make the ghosted view visible, so order is
// important here.
ghostView.visibility = View.INVISIBLE
- ghostedView.visibility = View.INVISIBLE
+
+ // Make the ghosted view invisible again. We use the transition visibility like
+ // GhostView does so that we don't mess up with the accessibility tree (see
+ // b/204944038#comment17).
+ ghostedView.setTransitionVisibility(View.INVISIBLE)
backgroundView.visibility = View.INVISIBLE
}
return
@@ -257,6 +261,10 @@
GhostView.removeGhost(ghostedView)
launchContainerOverlay.remove(backgroundView)
+
+ // Make sure that the view is considered VISIBLE by accessibility by first making it
+ // INVISIBLE then VISIBLE (see b/204944038#comment17 for more info).
+ ghostedView.visibility = View.INVISIBLE
ghostedView.visibility = View.VISIBLE
ghostedView.invalidate()
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
index 3bf6c5e..ebe96eb 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -27,25 +27,19 @@
import android.util.MathUtils
import android.view.View
import android.view.ViewGroup
-import android.view.animation.AnimationUtils
-import android.view.animation.PathInterpolator
+import android.view.animation.Interpolator
+import com.android.systemui.animation.Interpolators.LINEAR
import kotlin.math.roundToInt
private const val TAG = "LaunchAnimator"
/** A base class to animate a window launch (activity or dialog) from a view . */
-class LaunchAnimator @JvmOverloads constructor(
- context: Context,
- private val isForTesting: Boolean = false
+class LaunchAnimator(
+ private val timings: Timings,
+ private val interpolators: Interpolators
) {
companion object {
internal const val DEBUG = false
- const val ANIMATION_DURATION = 500L
- private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
- private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
- private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT
-
- private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f)
private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
/**
@@ -53,23 +47,20 @@
* sub-animation starting [delay] ms after the launch animation and that lasts [duration].
*/
@JvmStatic
- fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
+ fun getProgress(
+ timings: Timings,
+ linearProgress: Float,
+ delay: Long,
+ duration: Long
+ ): Float {
return MathUtils.constrain(
- (linearProgress * ANIMATION_DURATION - delay) / duration,
+ (linearProgress * timings.totalDuration - delay) / duration,
0.0f,
1.0f
)
}
}
- /** The interpolator used for the width, height, Y position and corner radius. */
- private val animationInterpolator = AnimationUtils.loadInterpolator(context,
- R.interpolator.launch_animation_interpolator_y)
-
- /** The interpolator used for the X position. */
- private val animationInterpolatorX = AnimationUtils.loadInterpolator(context,
- R.interpolator.launch_animation_interpolator_x)
-
private val launchContainerLocation = IntArray(2)
private val cornerRadii = FloatArray(8)
@@ -159,6 +150,45 @@
fun cancel()
}
+ /** The timings (durations and delays) used by this animator. */
+ class Timings(
+ /** The total duration of the animation. */
+ val totalDuration: Long,
+
+ /** The time to wait before fading out the expanding content. */
+ val contentBeforeFadeOutDelay: Long,
+
+ /** The duration of the expanding content fade out. */
+ val contentBeforeFadeOutDuration: Long,
+
+ /**
+ * The time to wait before fading in the expanded content (usually an activity or dialog
+ * window).
+ */
+ val contentAfterFadeInDelay: Long,
+
+ /** The duration of the expanded content fade in. */
+ val contentAfterFadeInDuration: Long
+ )
+
+ /** The interpolators used by this animator. */
+ data class Interpolators(
+ /** The interpolator used for the Y position, width, height and corner radius. */
+ val positionInterpolator: Interpolator,
+
+ /**
+ * The interpolator used for the X position. This can be different than
+ * [positionInterpolator] to create an arc-path during the animation.
+ */
+ val positionXInterpolator: Interpolator = positionInterpolator,
+
+ /** The interpolator used when fading out the expanding content. */
+ val contentBeforeFadeOutInterpolator: Interpolator,
+
+ /** The interpolator used when fading in the expanded content. */
+ val contentAfterFadeInInterpolator: Interpolator
+ )
+
/**
* Start a launch animation controlled by [controller] towards [endState]. An intermediary
* layer with [windowBackgroundColor] will fade in then fade out above the expanding view, and
@@ -221,8 +251,8 @@
// Update state.
val animator = ValueAnimator.ofFloat(0f, 1f)
- animator.duration = if (isForTesting) 0 else ANIMATION_DURATION
- animator.interpolator = Interpolators.LINEAR
+ animator.duration = timings.totalDuration
+ animator.interpolator = LINEAR
val launchContainerOverlay = launchContainer.overlay
var cancelled = false
@@ -260,8 +290,8 @@
// TODO(b/184121838): Use reverse interpolators to get the same path/arc as the non
// reversed animation.
val linearProgress = animation.animatedFraction
- val progress = animationInterpolator.getInterpolation(linearProgress)
- val xProgress = animationInterpolatorX.getInterpolation(linearProgress)
+ val progress = interpolators.positionInterpolator.getInterpolation(linearProgress)
+ val xProgress = interpolators.positionXInterpolator.getInterpolation(linearProgress)
val xCenter = MathUtils.lerp(startCenterX, endCenterX, xProgress)
val halfWidth = MathUtils.lerp(startWidth, endWidth, progress) / 2f
@@ -278,7 +308,12 @@
// The expanding view can/should be hidden once it is completely covered by the opening
// window.
- state.visible = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1
+ state.visible = getProgress(
+ timings,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration
+ ) < 1
applyStateToWindowBackgroundLayer(
windowBackgroundLayer,
@@ -337,14 +372,25 @@
// We first fade in the background layer to hide the expanding view, then fade it out
// with SRC mode to draw a hole punch in the status bar and reveal the opening window.
- val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT)
+ val fadeInProgress = getProgress(
+ timings,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration
+ )
if (fadeInProgress < 1) {
- val alpha = Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation(fadeInProgress)
+ val alpha =
+ interpolators.contentBeforeFadeOutInterpolator.getInterpolation(fadeInProgress)
drawable.alpha = (alpha * 0xFF).roundToInt()
} else {
val fadeOutProgress = getProgress(
- linearProgress, ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
- val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress)
+ timings,
+ linearProgress,
+ timings.contentAfterFadeInDelay,
+ timings.contentAfterFadeInDuration
+ )
+ val alpha =
+ 1 - interpolators.contentAfterFadeInInterpolator.getInterpolation(fadeOutProgress)
drawable.alpha = (alpha * 0xFF).roundToInt()
if (drawHole) {
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index a16f5cd..da9a92a 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -20,9 +20,11 @@
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;
import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -39,6 +41,7 @@
public interface BcSmartspaceDataPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
int VERSION = 1;
+ String TAG = "BcSmartspaceDataPlugin";
/** Register a listener to get Smartspace data. */
void registerListener(SmartspaceTargetListener listener);
@@ -124,10 +127,14 @@
/** Interface for launching Intents, which can differ on the lockscreen */
interface IntentStarter {
default void startFromAction(SmartspaceAction action, View v, boolean showOnLockscreen) {
- if (action.getIntent() != null) {
- startIntent(v, action.getIntent(), showOnLockscreen);
- } else if (action.getPendingIntent() != null) {
- startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ try {
+ if (action.getIntent() != null) {
+ startIntent(v, action.getIntent(), showOnLockscreen);
+ } else if (action.getPendingIntent() != null) {
+ startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Could not launch intent for action: " + action, e);
}
}
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml
new file mode 100644
index 0000000..230a256
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml
@@ -0,0 +1,44 @@
+<?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:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="1.5"
+ android:strokeAlpha="1"
+ android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="1.5"
+ android:strokeAlpha="1"
+ android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " />
+ </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
index c58e2e3..b3987f1 100644
--- a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
+++ b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
@@ -50,6 +50,11 @@
android:state_first="true"
android:state_single="true"
android:drawable="@drawable/ic_lock_aod" />
+ <item
+ android:id="@+id/unlocked_aod"
+ android:state_last="true"
+ android:state_single="true"
+ android:drawable="@drawable/ic_unlocked_aod" />
<item
android:id="@+id/no_icon"
@@ -79,4 +84,19 @@
android:fromId="@id/locked"
android:toId="@id/locked_aod"
android:drawable="@drawable/lock_ls_to_aod" />
+
+ <transition
+ android:fromId="@id/unlocked_aod"
+ android:toId="@id/unlocked"
+ android:drawable="@drawable/unlocked_aod_to_ls" />
+
+ <transition
+ android:fromId="@id/unlocked"
+ android:toId="@id/unlocked_aod"
+ android:drawable="@drawable/unlocked_ls_to_aod" />
+
+ <transition
+ android:fromId="@id/unlocked"
+ android:toId="@id/locked_aod"
+ android:drawable="@drawable/unlocked_to_aod_lock" />
</animated-selector>
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml
new file mode 100644
index 0000000..3b59ba8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml
@@ -0,0 +1,133 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="1.5"
+ android:strokeAlpha="1"
+ android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="1.5"
+ android:strokeAlpha="1"
+ android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="333" android:startOffset="0" android:valueFrom="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueTo="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="1.5"
+ android:valueTo="2"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.386,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueTo="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="1.5"
+ android:valueTo="2.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueTo="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml
new file mode 100644
index 0000000..1c6d0b5
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml
@@ -0,0 +1,136 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="2"
+ android:valueTo="1.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="2.5"
+ android:valueTo="1.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
new file mode 100644
index 0000000..b6d76e0
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
@@ -0,0 +1,169 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G_T_1" android:translateX="22.75" android:translateY="22.25" android:scaleX="1.02" android:scaleY="1.02">
+ <group android:name="_R_G_L_2_G" android:translateX="-8.75" android:translateY="-8.75">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#FF000000"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+ </group>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333" android:startOffset="0" android:valueFrom="2.5" android:valueTo="2" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="83" android:startOffset="0" android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.456,0 0.464,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData"
+ android:duration="133" android:startOffset="83" android:valueFrom="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueTo="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.606,0 0.035,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData"
+ android:duration="117" android:startOffset="217" android:valueFrom="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueTo="M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.511,0 0.409,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="333" android:startOffset="0" android:valueFrom="22.75" android:valueTo="23" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateY" android:duration="333" android:startOffset="0" android:valueFrom="22.25" android:valueTo="25.5" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX"
+ android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY"
+ android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333" android:startOffset="0" android:valueFrom="2" android:valueTo="1.5" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.38,0 0.274,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333" android:startOffset="0" android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333" android:startOffset="0" android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="850" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
index 1863d11..7c5dbc2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
@@ -30,8 +30,6 @@
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="44dp"
- android:paddingEnd="44dp"
android:visibility="gone"
android:textColor="?attr/wallpaperTextColor"
android:theme="@style/TextAppearance.Keyguard"
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 74a7123..9cf1793 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Toestel is handmatig gesluit"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nie herken nie"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nie herken nie"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Skakel "<b>"kameratoegang"</b>" in Instellings > Privaatheid aan om Gesigslot te gebruik"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_1">%d</xliff:g> pogings oor.</item>
<item quantity="one">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_0">%d</xliff:g> poging oor voordat jy jou diensverskaffer moet kontak om jou toestel te ontsluit.</item>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index ef97693..2d436c8 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"መሣሪያ በተጠቃሚው ራሱ ተቆልፏል"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"አልታወቀም"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"አልታወቀም"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"በመልክ መክፈትን ለመጠቀም "<b>"የካሜራ መዳረሻ"</b>"ን በቅንብሮች እና ግላዊነት ውስጥ ያብሩ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
<item quantity="other">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index d44a861..9c73b9d 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -105,6 +105,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"تم حظر الجهاز يدويًا"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"لم يتم التعرف عليها."</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"لم يتم التعرّف عليه."</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"لاستخدام ميزة \"فتح الجهاز بالتعرف على الوجه\"، عليك منح إذن "<b>"الوصول إلى الكاميرا"</b>" في الإعدادات > الخصوصية."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="zero">أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
<item quantity="two">أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index ab50326..e9c20b5 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ডিভাইচটো মেনুৱেলভাৱে লক কৰা হৈছিল"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"চিনাক্ত কৰিব পৰা নাই"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"চিনাক্ত কৰিব পৰা নাই"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ফেচ আনলক সুবিধাটো ব্যৱহাৰ কৰিবলৈ ছেটিং > গোপনীয়তাত "<b>"কেমেৰাৰ এক্সেছ"</b>" অন কৰক"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
<item quantity="other">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index 6153591..f9c67cb 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Cihaz əl ilə kilidləndi"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Tanınmır"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Tanınmır"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Üz ilə Kiliddən Açma funksiyasını istifadə etmək üçün Ayarlar > Məxfilik bölməsində "<b>"Kameraya girişi"</b>" aktiv edin"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM PIN-ni daxil edin. <xliff:g id="NUMBER_1">%d</xliff:g> cəhdiniz qalır.</item>
<item quantity="one">SIM PIN-ni daxil edin. Cihazınızı kiliddən çıxarmaq üçün operatorunuzla əlaqə saxlamadan öncə <xliff:g id="NUMBER_0">%d</xliff:g> cəhdiniz qalır.</item>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 52bbd4b..647e786 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -96,6 +96,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nije prepoznat"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznat"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Da biste koristili otključavanje licem, uključite "<b>"pristup kameri"</b>" u odeljku Podešavanja > Privatnost"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
<item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index d18fefa3..adcedff 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Прылада была заблакіравана ўручную"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Не распазнана"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Не распазнана"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Каб выкарыстоўваць распазнаванне твару, уключыце "<b>"доступ да камеры"</b>" праз раздзел \"Налады > Прыватнасць\""</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Увядзіце PIN-код SIM-карты. У вас засталася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
<item quantity="few">Увядзіце PIN-код SIM-карты. У вас засталося <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index 256672b..348b46c 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Устройството бе заключено ръчно"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Не е разпознато"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Не е разпознато"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"За да използвате функцията „Отключване с лице“, включете "<b>"достъпа до камерата"</b>" от „Настройки > Поверителност“"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Въведете ПИН кода за SIM картата – остават ви <xliff:g id="NUMBER_1">%d</xliff:g> опита.</item>
<item quantity="one">Въведете ПИН кода за SIM картата – остава ви <xliff:g id="NUMBER_0">%d</xliff:g> опит, преди да се наложи да се свържете с оператора си, за да отключите устройството.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 74fed29..0dc7052 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ডিভাইসটিকে ম্যানুয়ালি লক করা হয়েছে"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"শনাক্ত করা যায়নি"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"শনাক্ত করা যায়নি"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"\'ফেস আনলক\' ফিচার ব্যবহার করতে \'সেটিংস ও গোপনীয়তা\' বিকল্পে গিয়ে "<b>"ক্যামেরায় অ্যাক্সেস দিন"</b></string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
<item quantity="other">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index ef15ce3..86238b1 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -96,6 +96,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nije prepoznato"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznato"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Da koristite otključavanje licem, uključite "<b>"Pristup kameri"</b>" u meniju Postavke > Privatnost"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
<item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 5a71f37..5c315db 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositiu s\'ha bloquejat manualment"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"No s\'ha reconegut"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"No s\'ha reconegut"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Per utilitzar Desbloqueig facial, activa "<b>"Accés a la càmera"</b>" a Configuració > Privadesa"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Introdueix el PIN de la SIM. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents.</item>
<item quantity="one">Introdueix el PIN de la SIM. Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu.</item>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 41300fc..f5ef1bb 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Zařízení bylo ručně uzamčeno"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nerozpoznáno"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nerozpoznáno"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Pokud chcete používat odemknutí obličejem, v Nastavení > Soukromí zapnetě "<b>"přístup k fotoaparátu"</b></string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="few">Zadejte PIN SIM karty. Zbývají <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
<item quantity="many">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index 01ea136..c008f74 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheden blev låst manuelt"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ikke genkendt"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ikke genkendt"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Hvis du vil bruge ansigtslåsen, skal du aktivere "<b>"Kameraadgang"</b>" under Indstillinger > Privatliv"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
<item quantity="other">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 0d6728a..87c1bf4 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Gerät manuell gesperrt"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nicht erkannt"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nicht erkannt"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Wenn du die Entsperrung per Gesichtserkennung verwenden möchtest, aktiviere in den Einstellungen unter „Datenschutz“ die Option "<b>"Kamerazugriff"</b></string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche.</item>
<item quantity="one">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_0">%d</xliff:g> Versuch, bevor das Gerät nur noch vom Mobilfunkanbieter entsperrt werden kann.</item>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 518f2e2..f04747f 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Η συσκευή κλειδώθηκε με μη αυτόματο τρόπο"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Δεν αναγνωρίστηκε"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Δεν αναγνωρίστηκε"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Για να χρησιμοποιήσετε τη λειτουργία Ξεκλείδωμα με το πρόσωπο, ενεργοποιήστε την επιλογή "<b>"Πρόσβαση στην κάμερα"</b>" από το μενού Ρυθμίσεις > Απόρρητο"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένουν άλλες <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες.</item>
<item quantity="one">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένει άλλη <xliff:g id="NUMBER_0">%d</xliff:g> προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με τον πάροχο κινητής τηλεφωνίας, για να ξεκλειδώσετε τη συσκευή.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index e2db3b1..eb3a5be 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognised"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 6c9ddb8..9cb8227 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognised"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index e2db3b1..eb3a5be 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognised"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index e2db3b1..eb3a5be 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognised"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index 9c32604..cc61057 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Not recognized"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognized"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index 7be89b6..74db06d 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositivo se bloqueó de forma manual"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"No se reconoció"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"No se reconoció"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar Desbloqueo facial, activa el "<b>"Acceso a la cámara"</b>" en Configuración y privacidad"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Ingresa el PIN de la SIM. Quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos más.</item>
<item quantity="one">Ingresa el PIN de la SIM. Queda <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que debas comunicarte con tu proveedor para desbloquear el dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index b0f9f33..d833b06 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositivo se ha bloqueado manualmente"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"No se reconoce"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"No se reconoce"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar Desbloqueo Facial, habilita el "<b>"acceso a la cámara"</b>" en Ajustes y privacidad"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Introduce el PIN de la tarjeta SIM. Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
<item quantity="one">Introduce el PIN de la tarjeta SIM. Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para tener que ponerte en contacto con tu operador para desbloquear el dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index 58b870f..0cd86dd 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Seade lukustati käsitsi"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ei tuvastatud"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ei tuvastatud"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Näoga avamise kasutamiseks lülitage menüüs Seaded > Privaatsus sisse "<b>"juurdepääs kaamerale"</b>"."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_1">%d</xliff:g> katset.</item>
<item quantity="one">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_0">%d</xliff:g> katse enne, kui peate seadme avamiseks ühendust võtma operaatoriga.</item>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 2366156..580399d 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Eskuz blokeatu da gailua"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ez da ezagutu"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ez da ezagutu"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Aurpegi bidez desblokeatzeko aukera erabiltzeko, aktibatu "<b>"kamera atzitzeko baimena"</b>" Ezarpenak > Pribatutasuna atalean"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Idatzi SIMaren PINa. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item>
<item quantity="one">Idatzi SIMaren PINa. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index e110d08..a21507e 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"دستگاه بهصورت دستی قفل شده است"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"شناسایی نشد"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"شناسایی نشد"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"برای استفاده از «قفلگشایی با چهره»، "<b>"دسترسی به دوربین"</b>" را در «تنظیمات > حریمخصوصی» روشن کنید"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">پین سیمکارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
<item quantity="other">پین سیمکارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 82927e1..f0826e5 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Laite lukittiin manuaalisesti"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ei tunnistettu"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ei tunnistettu"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Jos haluat käyttää kasvojentunnistusavausta, valitse Asetukset > Yksityisyys ja laita "<b>"Pääsy kameraan"</b>" päälle"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Anna SIM-kortin PIN-koodi. Sinulla on <xliff:g id="NUMBER_1">%d</xliff:g> yritystä jäljellä.</item>
<item quantity="one">Anna SIM-kortin PIN-koodi. <xliff:g id="NUMBER_0">%d</xliff:g> yrityksen jälkeen laite lukittuu, ja vain operaattori voi avata sen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 3646394..dada709 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"L\'appareil a été verrouillé manuellement"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Doigt non reconnu"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Doigt non reconnu"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Pour utiliser le déverrouillage par reconnaissance faciale, activez l\'"<b>"accès à l\'appareil photo"</b>" dans Paramètres > Confidentialité"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
<item quantity="other">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index 6e1ee96..d19db43 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Appareil verrouillé manuellement"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Non reconnu"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Non reconnu"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Pour utiliser Face Unlock, activez "<b>"Accès à l\'appareil photo"</b>" dans Paramètres > Confidentialité"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentative restante.</item>
<item quantity="other">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentatives restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 123b8c1..bfbcf9d7 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo bloqueouse manualmente"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Non se recoñeceu"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Non se recoñeceu"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar o desbloqueo facial, activa "<b>"Acceso á cámara"</b>" en Configuración > Privacidade"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Introduce o código PIN da SIM. Quédanche <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
<item quantity="one">Introduce o código PIN da SIM. Quédache <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que teñas que contactar co operador para desbloquear o dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 1ca39d7..c804ec0 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ઉપકરણ મેન્યુઅલી લૉક કર્યું હતું"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ઓળખાયેલ નથી"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ઓળખાયેલ નથી"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ફેસ અનલૉક સુવિધાનો ઉપયોગ કરવા માટે, સેટિંગ > પ્રાઇવસીમાં જઈને "<b>"કૅમેરા ઍક્સેસ"</b>" ચાલુ કરો"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
<item quantity="other">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index e017a6b..a571a2a 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"डिवाइस को मैन्युअल रूप से लॉक किया गया था"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"पहचान नहीं हो पाई"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"पहचान नहीं हो पाई"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"फ़ेस अनलॉक की सुविधा का इस्तेमाल करने के लिए, सेटिंग और निजता में जाकर, "<b>"कैमरे का ऐक्सेस"</b>" चालू करें"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
<item quantity="other">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index 123f423..1a40d46 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -96,6 +96,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nije prepoznat"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznato"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Da biste koristili otključavanje licem, uključite opciju "<b>"Pristup kameri"</b>" u odjeljku Postavke > Privatnost"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
<item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index 47b49a4..47a1c61 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Az eszközt manuálisan lezárták"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nem ismerhető fel"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nem ismerhető fel"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Az Arcalapú feloldás funkció használatához kapcsolja be a "<b>"Hozzáférés a kamerához"</b>" beállítást a Beállítások > Adatvédelem szakaszban."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozása maradt.</item>
<item quantity="one">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozása maradt. Ha elfogynak a próbálkozási lehetőségek, az eszköz feloldásához fel kell vennie a kapcsolatot szolgáltatójával.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 16bbb07..923d762 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Սարքը կողպվել է ձեռքով"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Չհաջողվեց ճանաչել"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Չհաջողվեց ճանաչել"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Դեմքով ապակողպումն օգտագործելու համար անցեք Կարգավորումներ > Գաղտնիություն և տրամադրեք "<b>"տեսախցիկն օգտագործելու թույլտվություն"</b>"։"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
<item quantity="other">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index 757e6a5..559069b 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Perangkat dikunci secara manual"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Tidak dikenali"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Untuk menggunakan Face Unlock, aktifkan "<b>"Akses kamera"</b>" di Setelan > Privasi"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_1">%d</xliff:g> percobaan.</item>
<item quantity="one">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_0">%d</xliff:g> percobaan sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.</item>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 6dc8246..bec957f 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Tækinu var læst handvirkt"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Þekktist ekki"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Þekktist ekki"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Þú verður að kveikja á "<b>"aðgangi að myndavél"</b>" í „Stillingar > persónuvernd“ til að nota andlitskenni"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraun eftir.</item>
<item quantity="other">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraunir eftir.</item>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 337d433..656a8bc 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Il dispositivo è stato bloccato manualmente"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Non riconosciuto"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Non riconosciuto"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Per utilizzare lo sblocco con il volto, attiva "<b>"l\'accesso alla fotocamera"</b>" in Impostazioni > Privacy"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="other">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione.</item>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 1ba6f83..e241beb 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"המכשיר ננעל באופן ידני"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"לא זוהתה"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"לא זוהתה"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"כדי להשתמש בתכונה \'פתיחה ע\"י זיהוי הפנים\', יש להפעיל את ה"<b>"גישה למצלמה"</b>" בהגדרות > פרטיות"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="two">יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
<item quantity="many">יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 1acc14c..6e6adba 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"デバイスは手動でロックされました"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"認識されませんでした"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"認識されませんでした"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"顔認証を使用するには、[設定] > [プライバシー] で"<b>"カメラへのアクセス"</b>"を有効にしてください"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_1">%d</xliff:g> 回です。</item>
<item quantity="one">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_0">%d</xliff:g> 回です。この回数を超えた場合は、携帯通信会社にお問い合わせください。</item>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index 2f38e64..c728471 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"მოწყობილობა ხელით ჩაიკეტა"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"არ არის ამოცნობილი"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"არ არის ამოცნობილი"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"იმისთვის, რომ სახით განბლოკვით ისარგებლოთ, ჩართეთ "<b>"კამერაზე წვდომა"</b>" პარამეტრებსა და კონფიდენციალურობაში"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_1">%d</xliff:g> მცდელობა.</item>
<item quantity="one">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_0">%d</xliff:g> მცდელობა, რომლის შემდეგაც მოწყობილობის განსაბლოკად დაგჭირდებათ თქვენს ოპერატორთან დაკავშირება.</item>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index c7a1713..b192f02 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Құрылғы қолмен құлыпталды"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Танылмады"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Танылмады"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Face Unlock функциясын пайдалану үшін \"Параметрлер > Құпиялылық\" бөлімінен "<b>"Камераны пайдалану рұқсатын"</b>" қосыңыз."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
<item quantity="one">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 388d4fc..e31621e 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ឧបករណ៍ត្រូវបានចាក់សោដោយអ្នកប្រើផ្ទាល់"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"មិនអាចសម្គាល់បានទេ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"មិនអាចសម្គាល់បានទេ"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ដើម្បីប្រើមុខងារដោះសោតាមទម្រង់មុខ សូមបើក"<b>"ការចូលប្រើកាមេរ៉ា"</b>"នៅក្នុងការកំណត់ > ឯកជនភាព"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">បញ្ចូលកូដ PIN របស់ស៊ីម។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត។</item>
<item quantity="one">បញ្ចូលកូដ PIN របស់ស៊ីម។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="NUMBER_0">%d</xliff:g> ដងទៀត មុនពេលដែលអ្នកត្រូវទាក់ទងទៅក្រុមហ៊ុនសេវាទូរសព្ទរបស់អ្នកដើម្បីដោះសោឧបករណ៍របស់អ្នក។</item>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 9279fad..eeb8cbf 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ಸಾಧನವನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ಫೇಸ್ ಅನ್ಲಾಕ್ ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್ಗಳು > ಗೌಪ್ಯತೆ ಎಂಬಲ್ಲಿ "<b>"ಕ್ಯಾಮರಾ ಪ್ರವೇಶವನ್ನು"</b>" ಆನ್ ಮಾಡಿ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
<item quantity="other">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 761ccfa..e9b83d1 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"기기가 수동으로 잠금 설정되었습니다."</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"인식할 수 없음"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"인식할 수 없음"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"얼굴 인식 잠금 해제를 사용하려면 설정 > 개인 정보 보호에서 "<b>"카메라 액세스"</b>"를 사용 설정하세요."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM PIN을 입력하세요. 입력은 <xliff:g id="NUMBER_1">%d</xliff:g>번 더 시도할 수 있습니다.</item>
<item quantity="one">SIM PIN을 입력하세요. 입력에 <xliff:g id="NUMBER_0">%d</xliff:g>번 더 실패하면 이동통신사에 문의하여 기기를 잠금 해제해야 합니다.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 216c978..dd2c2f7 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Түзмөк кол менен кулпуланды"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Таанылган жок"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Таанылган жок"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Жөндөөлөр > Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM-картанын PIN кодун киргизиңиз. Сизде <xliff:g id="NUMBER_1">%d</xliff:g> аракет калды.</item>
<item quantity="one">SIM-картанын PIN кодун киргизиңиз. Сизде <xliff:g id="NUMBER_0">%d</xliff:g> аракет калды, андан кийин түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуна кайрылышыңыз керек болот.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index a9dd139..611c666 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ອຸປະກອນຖືກສັ່ງໃຫ້ລັອກ"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ບໍ່ຮູ້ຈັກ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ບໍ່ຮູ້ຈັກ"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ເພື່ອໃຊ້ການປົດລັອກດ້ວຍໜ້າ, ກະລຸນາເປີດໃຊ້ "<b>"ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ"</b>" ໃນການຕັ້ງຄ່າ > ຄວາມເປັນສ່ວນຕົວ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">ລະຫັດ SIM PIN ບໍ່ຖືກຕ້ອງ. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ.</item>
<item quantity="one">ໃສ່ລະຫັດ SIM PIN. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ຈະຕ້ອງຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການເພື່ອປົດລັອກ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 20164a1..08ddb74 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Įrenginys užrakintas neautomatiškai"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Neatpažinta"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Neatpažinta"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Jei norite naudoti atrakinimą pagal veidą, įjunkite parinktį "<b>"Prieiga prie fotoaparato"</b>" skiltyje „Nustatymai“ > „Privatumas“"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymas.</item>
<item quantity="few">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymai.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index 238b706..13fcfae 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -96,6 +96,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Ierīce tika bloķēta manuāli."</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nav atpazīts"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nav atpazīts"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Lai izmantotu autorizāciju pēc sejas, sadaļā Iestatījumi > Konfidencialitāte ieslēdziet opciju "<b>"Piekļuve kamerai"</b>"."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="zero">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
<item quantity="one">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index f4c3dde..5cf025c 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Уредот е заклучен рачно"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Непознат"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Непознат"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"За да користите „Отклучување со лик“, вклучете "<b>"Пристап до камерата"</b>" во „Поставки > Приватност“"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Внесете PIN-код за SIM-картичката. Ви преостанува уште <xliff:g id="NUMBER_1">%d</xliff:g> обид.</item>
<item quantity="other">Внесете PIN-код за SIM-картичката. Ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> обиди.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index 06228c6c..30e2081 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ഉപകരണം നേരിട്ട് ലോക്കുചെയ്തു"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"തിരിച്ചറിയുന്നില്ല"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"തിരിച്ചറിയുന്നില്ല"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ഫെയ്സ് അൺലോക്ക് ഉപയോഗിക്കാൻ, ക്രമീകരണം > സ്വകാര്യത എന്നതിൽ "<b>"ക്യാമറാ ആക്സസ്"</b>" ഓണാക്കുക"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">സിം പിൻ നൽകുക. നിങ്ങൾക്ക് <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു.</item>
<item quantity="one">സിം പിൻ നൽകുക. ഉപകരണം അൺലോക്ക് ചെയ്യാൻ കാരിയറുമായി ബന്ധപ്പെടേണ്ടിവരുന്നതിന് മുമ്പ് <xliff:g id="NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index db1396b..0ea710c 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Төхөөрөмжийг гараар түгжсэн"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Таньж чадсангүй"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Таньж чадсангүй"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Царайгаар түгжээ тайлахыг ашиглахын тулд Тохиргоо > Нууцлал хэсэгт "<b>" Камерын хандалтыг "</b>" асаана уу"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM-н ПИН кодыг оруулна уу. Танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого үлдлээ.</item>
<item quantity="one">SIM-н ПИН кодыг оруулна уу. Танд оператор компанитайгаа холбогдохгүйгээр төхөөрөмжийн түгжээг тайлах <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого үлдлээ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index b83ef6bb..647d132 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"डिव्हाइस मॅन्युअली लॉक केले होते"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ओळखले नाही"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ओळखले नाही"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"फेस अनलॉक वापरण्यासाठी, सेटिंग्ज > गोपनीयता येथे "<b>"कॅमेरा अॅक्सेस"</b>" सुरू करा"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">सिम पिन एंटर करा, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
<item quantity="one">सिम पिन एंटर करा. तुम्ही तुमचे डिव्हाइस अनलॉक करण्यासाठी तुमच्या वाहकाशी संपर्क साधण्यापूर्वी, तुमच्याकडे <xliff:g id="NUMBER_0">%d</xliff:g> प्रयत्न शिल्लक आहे.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 4205a2d..88dfd68 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Peranti telah dikunci secara manual"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Tidak dikenali"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Untuk menggunakan Buka Kunci Wajah, hidupkan "<b>"akses Kamera"</b>" dalam Tetapan > Privasi"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_1">%d</xliff:g> percubaan lagi.</item>
<item quantity="one">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_0">%d</xliff:g> percubaan lagi sebelum anda perlu menghubungi pembawa anda untuk membuka kunci peranti.</item>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 6ce5c05..de879b1 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"စက်ပစ္စည်းကို ကိုယ်တိုင်ကိုယ်ကျ လော့ခ်ချထားခဲ့သည်"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"မသိ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"မသိ"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို သုံးရန် "<b>"ကင်မရာ သုံးခွင့်"</b>" ကို ‘ဆက်တင်များ > ကန့်သတ်ဆက်တင်’ တွင်ဖွင့်ပါ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">ဆင်းမ်ကတ် ပင်နံပါတ် ထည့်ပါ။ <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
<item quantity="one">ဆင်းမ်ကတ် ပင်နံပါတ် ထည့်ပါ။ သင့်စက်ကို လော့ခ်ဖွင့်ပေးရန်အတွက် ဝန်ဆောင်မှုပေးသူသို့ မဆက်သွယ်မီ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 890afc0..c7d6613 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheten ble låst manuelt"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ikke gjenkjent"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ikke gjenkjent"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"For å bruke ansiktslås, slå på "<b>"Kameratilgang"</b>" i Innstillinger > Personvern"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøk igjen.</item>
<item quantity="one">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_0">%d</xliff:g> forsøk igjen før du må kontakte operatøren din for å låse opp enheten.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 57d9d95..16ece5d 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"यन्त्रलाई म्यानुअल तरिकाले लक गरिएको थियो"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"पहिचान भएन"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"पहिचान भएन"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"फेस अनलक प्रयोग गर्न \"सेटिङ तथा गोपनीयता\" मा गई "<b>"क्यामेरा प्रयोग गर्ने अनुमति"</b>" दिनुहोस्"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM को PIN प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्।</item>
<item quantity="one">SIM को PIN प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि भने आफ्नो डिभाइस अनलक गर्नका लागि तपाईंले अनिवार्य रूपमा आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नु पर्ने हुन्छ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 11f29ef..bf1906d 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Apparaat is handmatig vergrendeld"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Niet herkend"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Niet herkend"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Als je Ontgrendelen via gezichtsherkenning wilt gebruiken, zet je "<b>"Cameratoegang"</b>" aan via Instellingen > Privacy"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
<item quantity="one">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat je contact met je provider moet opnemen om het apparaat te ontgrendelen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index 8a6c2449..3b20dcb 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ଡିଭାଇସ୍ ମାନୁଆଲ ଭାବେ ଲକ୍ କରାଗଲା"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ଫେସ ଅନଲକ ବ୍ୟବହାର କରିବା ପାଇଁ, ସେଟିଂସ ଏବଂ ଗୋପନୀୟତାରେ "<b>"କ୍ୟାମେରା ଆକ୍ସେସ"</b>"କୁ ଚାଲୁ କରନ୍ତୁ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM PIN ପ୍ରବେଶ କରନ୍ତୁ। ଆପଣଙ୍କ ପାଇଁ <xliff:g id="NUMBER_1">%d</xliff:g>ଟି ପ୍ରୟାସ ବଳକା ଅଛି।</item>
<item quantity="one">SIM PIN ପ୍ରବେଶ କରନ୍ତୁ। ଆପଣଙ୍କ ଡିଭାଇସ୍କୁ ଅନଲକ୍ କରିବା ପାଇଁ ପାଖରେ ବଳକା ଥିବା <xliff:g id="NUMBER_0">%d</xliff:g>ଟି ପ୍ରୟାସର ବ୍ୟବହାର କରିବା ପୂର୍ବରୁ ନିଜର କେରିଅର୍ଙ୍କୁ ସମ୍ପର୍କ କରନ୍ତୁ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index f40e09e..afc8897 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ਡੀਵਾਈਸ ਨੂੰ ਹੱਥੀਂ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ਫ਼ੇਸ ਅਣਲਾਕ ਨੂੰ ਵਰਤਣ ਲਈ, ਸੈਟਿੰਗਾਂ > ਪਰਦੇਦਾਰੀ ਵਿੱਚ ਜਾ ਕੇ "<b>"ਕੈਮਰਾ ਪਹੁੰਚ"</b>" ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
<item quantity="other">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 2d53dcc..0b45e36 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Urządzenie zostało zablokowane ręcznie"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nie rozpoznano"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nie rozpoznano"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Aby używać rozpoznawania twarzy, włącz "<b>"dostęp do aparatu"</b>" w Ustawieniach i prywatności"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="few">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
<item quantity="many">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> prób.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 9fd9b1f..d69b62b 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Não reconhecido"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações > Privacidade"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
<item quantity="other">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index da663f0..d3f5802 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Não reconhecido."</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido."</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para utilizar o Desbloqueio facial, ative o "<b>"Acesso à câmara"</b>" em Definições > Privacidade"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de ser necessário contactar o operador para desbloquear o dispositivo.</item>
<item quantity="other">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 9fd9b1f..d69b62b 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Não reconhecido"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações > Privacidade"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
<item quantity="other">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index be6aea0..2671076 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -96,6 +96,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Dispozitivul a fost blocat manual"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nu este recunoscută"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nu este recunoscut"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Pentru a folosi Deblocarea facială, activați "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="few">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări.</item>
<item quantity="other">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index 6e11d61..a91eef5 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Устройство было заблокировано вручную"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Не распознано"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Не распознано"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Чтобы использовать фейсконтроль, разрешите "<b>"доступ к камере"</b>". Для этого перейдите в настройки и нажмите \"Конфиденциальность\"."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Введите PIN-код. Осталась <xliff:g id="NUMBER_1">%d</xliff:g> попытка.</item>
<item quantity="few">Введите PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 902127d..d06c4a9 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"උපාංගය හස්තීයව අගුලු දමන ලදී"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"හඳුනා නොගන්නා ලදී"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"හඳුනා නොගන්නා ලදී"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"මුහුණෙන් අගුලු හැරීම භාවිත කිරීමට, සැකසීම් > පෞද්ගලිකත්වය තුළ "<b>"කැමරා ප්රවේශය"</b>" ක්රියාත්මක කරන්න"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
<item quantity="other">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index 5e4c248..a48123e 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Zariadenie bolo uzamknuté ručne"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nerozpoznané"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nerozpoznané"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Ak chcete používať odomknutie tvárou, v sekcii Nastavenia > Ochrana súkromia zapnite "<b>"prístup ku kamere"</b></string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="few">Zadajte kód PIN SIM karty. Zostávajú vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
<item quantity="many">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 149ac8e..4af9fe5 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Naprava je bila ročno zaklenjena"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ni prepoznano"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ni prepoznano"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Če želite uporabljati odklepanje z obrazom, v meniju »Nastavitve« > »Zasebnost« vklopite možnost "<b>"Dostop do fotoaparata"</b>"."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskus.</item>
<item quantity="two">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusa.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index 5fca45b..524f1c3 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Pajisja është kyçur manualisht"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Nuk njihet"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nuk njihet"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Për të përdorur \"Shkyçjen me fytyrë\", aktivizo "<b>"Qasjen te kamera"</b>" te \"Cilësimet\" > \"Privatësia\""</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Fut kodin PIN të kartës SIM. Të kanë mbetur edhe <xliff:g id="NUMBER_1">%d</xliff:g> tentativa.</item>
<item quantity="one">Fut kodin PIN të kartës SIM. Të ka mbetur edhe <xliff:g id="NUMBER_0">%d</xliff:g> tentativë para se të duhet të kontaktosh me operatorin tënd celular për ta shkyçur pajisjen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 7facbfb..0bb2cb4 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -96,6 +96,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Уређај је ручно закључан"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Није препознат"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Није препознат"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Да бисте користили откључавање лицем, укључите "<b>"приступ камери"</b>" у одељку Подешавања > Приватност"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај.</item>
<item quantity="few">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index f271dda..2af9bb5 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheten har låsts manuellt"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Identifierades inte"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Identifierades inte"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Om du vill använda ansiktslås aktiverar du "<b>"Kameraåtkomst"</b>" i Inställningar > Integritet"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_1">%d</xliff:g> försök återstår.</item>
<item quantity="one">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_0">%d</xliff:g> försök återstår innan du måste kontakta operatören för att låsa upp enheten.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index c84bef0..aaad8b9 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Umefunga kifaa mwenyewe"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Haitambuliwi"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Haitambuliwi"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Ili utumie kipengele cha kufungua kwa uso, washa kipengele cha "<b>"ufikiaji wa Kamera"</b>" katika Mipangilio na Faragha"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Weka PIN ya SIM. Zimesalia mara <xliff:g id="NUMBER_1">%d</xliff:g> za kujaribu.</item>
<item quantity="one">Weka PIN ya SIM. Ukijaribu tena mara <xliff:g id="NUMBER_0">%d</xliff:g> bila kufaulu, kifaa chako kitafungwa na utalazimika uwasiliane na mtoa huduma wako ili akifungue.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index e9bd638..e80cfaf 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -25,6 +25,9 @@
<!-- Margin around the various security views -->
<dimen name="keyguard_security_view_top_margin">12dp</dimen>
+ <!-- Padding for the lock icon on the keyguard -->
+ <dimen name="lock_icon_padding">16dp</dimen>
+
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">100dp</dimen>
<dimen name="widget_label_font_size">18sp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 151279e..ed9a5d4 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"பயனர் சாதனத்தைப் பூட்டியுள்ளார்"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"அடையாளங்காணபடவில்லை"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"அடையாளங்காணபடவில்லை"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"முகம் காட்டித் திறத்தல் அம்சத்தைப் பயன்படுத்த, அமைப்புகள் > தனியுரிமை என்பதற்குச் சென்று "<b>"கேமரா அணுகலை"</b>" இயக்கவும்"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">சிம் பின்னை உள்ளிடவும். மேலும், <xliff:g id="NUMBER_1">%d</xliff:g> வாய்ப்புகள் மீதமுள்ளன.</item>
<item quantity="one">சிம் பின்னை உள்ளிடவும். மீதமுள்ள <xliff:g id="NUMBER_0">%d</xliff:g> வாய்ப்பில் தவறுதலான பின் உள்ளிடப்பட்டால், உங்கள் தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்பு கொண்டு மட்டுமே சாதனத்தை அன்லாக் செய்ய முடியும்.</item>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 30f3c83..0c184ab 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -86,13 +86,14 @@
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత నమూనాను గీయాలి"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"డివైజ్ను పునఃప్రారంభించిన తర్వాత పిన్ నమోదు చేయాలి"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత పాస్వర్డ్ను నమోదు చేయాలి"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="9170360502528959889">"అదనపు భద్రత కోసం నమూనాని గీయాలి"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="5945186097160029201">"అదనపు భద్రత కోసం పిన్ నమోదు చేయాలి"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="2258263949430384278">"అదనపు భద్రత కోసం పాస్వర్డ్ని నమోదు చేయాలి"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="9170360502528959889">"అదనపు సెక్యూరిటీ కోసం ఆకృతి అవసరం"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="5945186097160029201">"అదనపు సెక్యూరిటీ కోసం పిన్ ఎంటర్ చేయాలి"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="2258263949430384278">"అదనపు సెక్యూరిటీ కోసం పాస్వర్డ్ను ఎంటర్ చేయాలి"</string>
<string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"పరికరం నిర్వాహకుల ద్వారా లాక్ చేయబడింది"</string>
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"పరికరం మాన్యువల్గా లాక్ చేయబడింది"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"గుర్తించలేదు"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"గుర్తించలేదు"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"ఫేస్ అన్లాక్ను ఉపయోగించడానికి, సెట్టింగ్లు > గోప్యతలో "<b>"కెమెరా యాక్సెస్"</b>"ను ఆన్ చేయండి"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM పిన్ని నమోదు చేయండి. మీకు <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నలు మిగిలి ఉన్నాయి.</item>
<item quantity="one">SIM పిన్ని నమోదు చేయండి, మీరు మీ పరికరాన్ని అన్లాక్ చేయడానికి తప్పనిసరిగా మీ క్యారియర్ను సంప్రదించడానికి ముందు మీకు <xliff:g id="NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది.</item>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index 25968b7..4d3c0b5 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"มีการล็อกอุปกรณ์ด้วยตัวเอง"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"ไม่รู้จัก"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ไม่รู้จัก"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"หากต้องการใช้ปลดล็อกด้วยใบหน้า ให้เปิด"<b>"การเข้าถึงกล้อง"</b>"ในการตั้งค่าและความเป็นส่วนตัว"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง</item>
<item quantity="one">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์</item>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index 8927c9e..4c391e5 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Manual na na-lock ang device"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Hindi nakilala"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Hindi nakilala"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Para magamit ang Pag-unlock Gamit ang Mukha, i-on ang "<b>"Access sa camera"</b>" sa Mga Setting > Privacy"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> natitirang pagsubok.</item>
<item quantity="other">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> na natitirang pagsubok.</item>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index 5ba351a..100f074d 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Cihazın manuel olarak kilitlendi"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Tanınmadı"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Tanınmadı"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Yüz Tanıma Kilidi\'ni kullanmak için Ayarlar > Gizlilik bölümünden "<b>"Kamera erişimi"</b>"\'ni açın"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM PIN\'inizi girin. <xliff:g id="NUMBER_1">%d</xliff:g> deneme hakkınız kaldı.</item>
<item quantity="one">SIM PIN\'inizi girin. Cihazınızın kilidini açmak için operatörünüzle bağlantı kurmak zorunda kalmadan önce <xliff:g id="NUMBER_0">%d</xliff:g> deneme hakkınız kaldı.</item>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 96f2ea2..a915f57 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -99,6 +99,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Пристрій заблоковано вручну"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Не розпізнано"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Не розпізнано"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Щоб використовувати фейсконтроль, увімкніть "<b>"Доступ до камери"</b>" в розділі \"Налаштування\" > \"Конфіденційність\""</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Введіть PIN-код SIM-карти. Залишилася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
<item quantity="few">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 3ae43f9..66bc9d6 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"آلہ کو دستی طور پر مقفل کیا گیا تھا"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"تسلیم شدہ نہیں ہے"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"تسلیم شدہ نہیں ہے"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"فیس اَنلاک کا استعمال کرنے کے لیے، ترتیبات اور رازداری میں "<b>"کیمرے تک رسائی"</b>" کو آن کریں"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔</item>
<item quantity="one">SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_0">%d</xliff:g> کوشش بچی ہے، اس کے بعد آپ کو اپنا آلہ غیر مقفل کرنے کے لیے اپنے کیریئر سے رابطہ کرنا ہوگا۔</item>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 5d281fa..1046dbd 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Qurilma qo‘lda qulflangan"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Aniqlanmadi"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Aniqlanmadi"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Yuz bilan ochish uchun Sozlamalar va maxfiylik orqali "<b>"kameraga kirishga ruxsat bering"</b></string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">SIM PIN kodini kiriting, sizda <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish bor.</item>
<item quantity="one">SIM PIN kodini kiriting, qurilmani qulfdan chiqarish uchun sizda <xliff:g id="NUMBER_0">%d</xliff:g> ta urinish bor.</item>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 52d64b9..569b99e 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Thiết bị đã bị khóa theo cách thủ công"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Không nhận dạng được"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Không nhận dạng được"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Để dùng tính năng Mở khoá bằng khuôn mặt, hãy bật tuỳ chọn "<b>"Truy cập máy ảnh"</b>" trong phần Cài đặt > Quyền riêng tư"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_1">%d</xliff:g> lần thử.</item>
<item quantity="one">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_0">%d</xliff:g> lần thử trước khi phải liên hệ với nhà mạng để mở khóa thiết bị của mình.</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index 4be7908..e429994 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"此设备已手动锁定"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"无法识别"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"无法识别"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"如需使用人脸解锁功能,请在“设置”>“隐私权”中开启"<b>"摄像头使用权限"</b></string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
<item quantity="one">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍不正确,则需要联系运营商帮您解锁设备。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index 7ca7677..d870304 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"使用者已手動將裝置上鎖"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"未能識別"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"未能識別"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"如要使用「面孔解鎖」,請在 [設定] > [私隱] 開啟"<b>"相機存取權"</b></string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
<item quantity="one">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然輸入錯誤,您必須聯絡流動網絡供應商解鎖您的裝置。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index d3802c5..99a00fd 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"裝置已手動鎖定"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"無法識別"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"無法識別"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"如要使用人臉解鎖功能,請前往「設定」>「隱私權」開啟"<b>"攝影機存取權"</b></string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="other">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
<item quantity="one">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然失敗,就必須請電信業者為裝置解鎖。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 5440b00..4c1ca1c 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -93,6 +93,7 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Idivayisi ikhiywe ngokwenza"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Akwaziwa"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Akwaziwa"</string>
+ <string name="kg_face_sensor_privacy_enabled" msgid="6513157891227284806">"Ukuze usebenzise Ukuvula ngobuso, vula "<b>"Ukufinyelela kwekhamera"</b>" kokuthi Amasethingi > Ubumfihlo"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
<item quantity="other">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 4fc38a8..1601043 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -219,6 +219,9 @@
<!-- Face hint message when finger was not recognized. [CHAR LIMIT=20] -->
<string name="kg_face_not_recognized">Not recognized</string>
+ <!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] -->
+ <string name="kg_face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string>
+
<!-- Instructions telling the user remaining times when enter SIM PIN view. -->
<plurals name="kg_password_default_pin_message">
<item quantity="one">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index b0bdc72..b8770e8 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -112,7 +112,7 @@
<style name="TextAppearance.Keyguard">
<item name="android:textSize">@dimen/widget_title_font_size</item>
<item name="android:lineHeight">@dimen/widget_title_line_height</item>
- <item name="android:gravity">center</item>
+ <item name="android:gravity">start</item>
<item name="android:ellipsize">end</item>
<item name="android:maxLines">2</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
@@ -131,6 +131,7 @@
<style name="TextAppearance.Keyguard.BottomArea">
<item name="android:textSize">14sp</item>
<item name="android:maxLines">1</item>
+ <item name="android:gravity">center</item>
<item name="android:textColor">?attr/wallpaperTextColor</item>
<item name="android:shadowColor">@color/keyguard_shadow_color</item>
<item name="android:shadowRadius">?attr/shadowRadius</item>
diff --git a/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml b/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml
deleted file mode 100644
index 9a69b33..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="?android:attr/textColorPrimary"
- android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
deleted file mode 100644
index 50267fd..0000000
--- a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle">
- <stroke
- android:color="?androidprv:attr/colorAccentPrimaryVariant"
- android:width="1dp"/>
- <corners android:radius="20dp"/>
- <padding
- android:left="8dp"
- android:right="8dp"
- android:top="4dp"
- android:bottom="4dp" />
- <solid android:color="@android:color/transparent" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
index 363a022..eb08434 100644
--- a/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
+++ b/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
@@ -13,17 +13,20 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle">
- <stroke
- android:color="?androidprv:attr/colorAccentPrimaryVariant"
- android:width="1dp"/>
- <corners android:radius="20dp"/>
- <padding
- android:left="16dp"
- android:right="16dp"
- android:top="8dp"
- android:bottom="8dp" />
- <solid android:color="@android:color/transparent" />
-</shape>
+ android:insetBottom="6dp"
+ android:insetTop="6dp">
+ <shape android:shape="rectangle">
+ <stroke
+ android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ android:width="1dp"/>
+ <corners android:radius="20dp"/>
+ <padding
+ android:left="16dp"
+ android:right="16dp"
+ android:top="8dp"
+ android:bottom="8dp"/>
+ <solid android:color="@android:color/transparent"/>
+ </shape>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
index 1a128df..14cb1de9 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
@@ -16,8 +16,8 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:insetTop="@dimen/qs_dialog_button_vertical_inset"
- android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+ android:insetTop="@dimen/dialog_button_vertical_inset"
+ android:insetBottom="@dimen/dialog_button_vertical_inset">
<ripple android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
@@ -29,10 +29,10 @@
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
<solid android:color="?androidprv:attr/colorAccentPrimary"/>
- <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
- android:top="@dimen/qs_dialog_button_vertical_padding"
- android:right="@dimen/qs_dialog_button_horizontal_padding"
- android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+ <padding android:left="@dimen/dialog_button_horizontal_padding"
+ android:top="@dimen/dialog_button_vertical_padding"
+ android:right="@dimen/dialog_button_horizontal_padding"
+ android:bottom="@dimen/dialog_button_vertical_padding"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index 467c20f..a47299d 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -16,8 +16,8 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:insetTop="@dimen/qs_dialog_button_vertical_inset"
- android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+ android:insetTop="@dimen/dialog_button_vertical_inset"
+ android:insetBottom="@dimen/dialog_button_vertical_inset">
<ripple android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
@@ -29,13 +29,13 @@
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
<solid android:color="@android:color/transparent"/>
- <stroke android:color="?androidprv:attr/colorAccentPrimary"
+ <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
android:width="1dp"
/>
- <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
- android:top="@dimen/qs_dialog_button_vertical_padding"
- android:right="@dimen/qs_dialog_button_horizontal_padding"
- android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+ <padding android:left="@dimen/dialog_button_horizontal_padding"
+ android:top="@dimen/dialog_button_vertical_padding"
+ android:right="@dimen/dialog_button_horizontal_padding"
+ android:bottom="@dimen/dialog_button_vertical_padding"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml b/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml
deleted file mode 100644
index 59a31e8..0000000
--- a/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle">
- <stroke
- android:color="?androidprv:attr/colorAccentPrimary"
- android:width="1dp"/>
- <corners android:radius="24dp"/>
- <padding
- android:left="16dp"
- android:right="16dp"
- android:top="8dp"
- android:bottom="8dp" />
- <solid android:color="@android:color/transparent" />
-</shape>
diff --git a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
new file mode 100644
index 0000000..a3e289a
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
@@ -0,0 +1,63 @@
+<?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.
+ -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@*android:id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollIndicators="top|bottom"
+ android:fillViewport="true"
+ android:paddingTop="@dimen/dialog_button_bar_top_padding"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ android:paddingBottom="@dimen/dialog_bottom_padding"
+ style="?android:attr/buttonBarStyle">
+ <com.android.internal.widget.ButtonBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale"
+ android:orientation="horizontal"
+ android:gravity="bottom">
+
+ <Button
+ android:id="@android:id/button3"
+ style="?android:attr/buttonBarNeutralButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Space
+ android:id="@*android:id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+ <Button
+ android:id="@android:id/button2"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@android:id/button1"
+ style="?android:attr/buttonBarPositiveButtonStyle"
+ android:layout_marginStart="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </com.android.internal.widget.ButtonBarLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
new file mode 100644
index 0000000..f280cbd
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
@@ -0,0 +1,91 @@
+<?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.
+ -->
+<com.android.internal.widget.AlertDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@*android:id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|top"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/dialog_top_padding"
+ >
+
+ <include layout="@layout/alert_dialog_title_systemui" />
+
+ <FrameLayout
+ android:id="@*android:id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ >
+
+ <ScrollView
+ android:id="@*android:id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Space
+ android:id="@*android:id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+
+ <TextView
+ android:id="@*android:id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.Dialog.Body.Message" />
+
+ <Space
+ android:id="@*android:id/textSpacerNoButtons"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="6dp" />
+ </LinearLayout>
+ </ScrollView>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@*android:id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ >
+
+ <FrameLayout
+ android:id="@*android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/alert_dialog_button_bar_systemui" />
+
+</com.android.internal.widget.AlertDialogLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
new file mode 100644
index 0000000..88f13b4
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
@@ -0,0 +1,62 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@*android:id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+>
+
+ <!-- If the client uses a customTitle, it will be added here. -->
+
+ <LinearLayout
+ android:id="@*android:id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_horizontal|top">
+
+ <ImageView
+ android:id="@*android:id/icon"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginBottom="16dp"
+ android:scaleType="fitCenter"
+ android:src="@null"
+ android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+ />
+
+ <TextView
+ android:id="@*android:id/alertTitle"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ style="@style/TextAppearance.Dialog.Title" />
+ </LinearLayout>
+
+ <Space
+ android:id="@*android:id/titleDividerNoCustom"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 3c9e44e..89690e8 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -73,6 +73,9 @@
android:accessibilityLiveRegion="polite"
android:singleLine="true"
android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:scrollHorizontally="true"
+ android:fadingEdge="horizontal"
android:textColor="@color/biometric_dialog_gray"/>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 86e2661..e69582f 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -34,9 +34,10 @@
<TextView
android:id="@+id/internet_dialog_title"
+ android:ellipsize="end"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
- android:layout_height="32dp"
+ android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.InternetDialog"
android:textSize="24sp"/>
@@ -44,7 +45,7 @@
android:id="@+id/internet_dialog_subtitle"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
@@ -52,22 +53,22 @@
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="@dimen/internet_dialog_progress_bar_width"
android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/internet_dialog_network_layout_margin"
android:orientation="vertical">
<View
android:id="@+id/divider"
- android:layout_gravity="center_vertical|center_horizontal"
- android:layout_width="340dp"
+ android:layout_width="match_parent"
android:layout_height="4dp"
+ android:layout_gravity="center_vertical|center_horizontal"
android:background="?androidprv:attr/colorSurfaceVariant"/>
<ProgressBar
android:id="@+id/wifi_searching_progress"
- android:indeterminate="true"
- android:layout_width="340dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:visibility="gone"
@@ -150,12 +151,27 @@
android:gravity="start|center_vertical">
<TextView
android:id="@+id/mobile_title"
+ android:maxLines="1"
style="@style/InternetDialog.NetworkTitle"/>
<TextView
android:id="@+id/mobile_summary"
style="@style/InternetDialog.NetworkSummary"/>
+ <TextView
+ android:id="@+id/airplane_mode_summary"
+ android:text="@string/airplane_mode"
+ android:visibility="gone"
+ style="@style/InternetDialog.NetworkSummary"/>
</LinearLayout>
+ <View
+ android:id="@+id/mobile_toggle_divider"
+ android:layout_width="1dp"
+ android:layout_height="28dp"
+ android:layout_marginStart="7dp"
+ android:layout_marginEnd="16dp"
+ android:layout_gravity="center_vertical"
+ android:background="?android:attr/textColorSecondary"/>
+
<FrameLayout
android:layout_width="@dimen/settingslib_switch_track_width"
android:layout_height="48dp"
@@ -177,7 +193,7 @@
<LinearLayout
android:id="@+id/turn_on_wifi_layout"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:gravity="center"
android:clickable="false"
android:focusable="false">
@@ -219,7 +235,7 @@
<LinearLayout
android:id="@+id/wifi_connected_layout"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:paddingStart="20dp"
android:paddingEnd="24dp"
android:background="@drawable/settingslib_switch_bar_bg_on"
@@ -241,7 +257,7 @@
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:layout_marginEnd="30dp"
android:layout_weight="1"
android:gravity="start|center_vertical">
@@ -362,26 +378,48 @@
android:clickable="true"/>
</LinearLayout>
</LinearLayout>
-
<FrameLayout
- android:id="@+id/done_layout"
- android:layout_width="67dp"
- android:layout_height="48dp"
- android:layout_marginEnd="24dp"
- android:layout_marginBottom="40dp"
- android:layout_gravity="end|center_vertical"
- android:clickable="true"
- android:focusable="true">
- <Button
- android:text="@string/inline_done_button"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="match_parent"
- android:layout_height="36dp"
- android:layout_gravity="center"
- android:textAppearance="@style/TextAppearance.InternetDialog"
- android:textSize="14sp"
- android:background="@drawable/internet_dialog_footer_background"
- android:clickable="false"/>
+ android:id="@+id/button_layout"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="@dimen/dialog_side_padding"
+ android:layout_marginEnd="@dimen/dialog_side_padding"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
+ android:clickable="false"
+ android:focusable="false">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/apm_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/turn_off_airplane_mode"
+ android:ellipsize="end"
+ style="@style/Widget.Dialog.Button.BorderButton"
+ android:clickable="true"
+ android:focusable="true"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_gravity="end|center_vertical">
+ <Button
+ android:id="@+id/done_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/inline_done_button"
+ style="@style/Widget.Dialog.Button"
+ android:clickable="true"
+ android:focusable="true"/>
+ </FrameLayout>
</FrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
index 868331e..f6a2136 100644
--- a/packages/SystemUI/res/layout/internet_list_item.xml
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -25,7 +25,7 @@
<LinearLayout
android:id="@+id/wifi_list"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:paddingStart="20dp"
android:paddingEnd="24dp">
<FrameLayout
@@ -45,7 +45,7 @@
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:layout_marginEnd="30dp"
android:layout_weight="1"
android:gravity="start|center_vertical">
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 8dbd59d..759670e 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -17,7 +17,6 @@
<com.android.systemui.statusbar.phone.KeyguardBottomAreaView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/keyguard_bottom_area"
android:layout_height="match_parent"
android:layout_width="match_parent"
@@ -114,7 +113,8 @@
android:layout_height="match_parent">
<include layout="@layout/keyguard_bottom_area_overlay" />
-
</FrameLayout>
+ <include layout="@layout/ambient_indication"
+ android:id="@+id/ambient_indication_container" />
</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index a64ef3e..3a186d2 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -24,9 +24,9 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="96dp"
+ android:layout_height="wrap_content"
android:gravity="start|center_vertical"
- android:paddingStart="16dp"
+ android:paddingStart="24dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/header_icon"
@@ -36,7 +36,7 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="20dp"
android:paddingBottom="24dp"
@@ -59,7 +59,7 @@
android:gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
- android:textColor="?android:attr/textColorTertiary"
+ android:textColor="?android:attr/textColorSecondary"
android:fontFamily="roboto-regular"
android:textSize="16sp"/>
</LinearLayout>
@@ -89,17 +89,16 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
- android:layout_marginStart="24dp"
- android:layout_marginBottom="18dp"
- android:layout_marginEnd="24dp"
+ android:layout_marginStart="@dimen/dialog_side_padding"
+ android:layout_marginEnd="@dimen/dialog_side_padding"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
android:orientation="horizontal">
<Button
android:id="@+id/stop"
- style="@style/MediaOutputRoundedOutlinedButton"
+ style="@style/Widget.Dialog.Button.BorderButton"
android:layout_width="wrap_content"
- android:layout_height="36dp"
- android:minWidth="0dp"
+ android:layout_height="wrap_content"
android:text="@string/keyboard_key_media_stop"
android:visibility="gone"/>
@@ -110,10 +109,9 @@
<Button
android:id="@+id/done"
- style="@style/MediaOutputRoundedOutlinedButton"
+ style="@style/Widget.Dialog.Button"
android:layout_width="wrap_content"
- android:layout_height="36dp"
- android:minWidth="0dp"
+ android:layout_height="wrap_content"
android:text="@string/inline_done_button"/>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/privacy_dialog.xml b/packages/SystemUI/res/layout/privacy_dialog.xml
index ee4530c..9368a6a 100644
--- a/packages/SystemUI/res/layout/privacy_dialog.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog.xml
@@ -22,10 +22,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/ongoing_appops_dialog_side_margins"
android:layout_marginEnd="@dimen/ongoing_appops_dialog_side_margins"
- android:layout_marginTop="8dp"
android:orientation="vertical"
android:paddingBottom="8dp"
- android:paddingTop="12dp"
+ android:paddingTop="8dp"
android:paddingHorizontal="@dimen/ongoing_appops_dialog_side_padding"
android:background="@drawable/qs_dialog_bg"
/>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 2ac03c2..f5c6036 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -56,17 +56,4 @@
layout="@layout/qs_customize_panel"
android:visibility="gone" />
- <ImageView
- android:id="@+id/qs_drag_handle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="24dp"
- android:elevation="4dp"
- android:importantForAccessibility="no"
- android:scaleType="center"
- android:src="@drawable/ic_qs_drag_handle"
- android:tint="@color/qs_detail_button_white"
- tools:ignore="UseAppTint" />
-
</com.android.systemui.qs.QSContainerImpl>
diff --git a/packages/SystemUI/res/layout/qs_user_detail.xml b/packages/SystemUI/res/layout/qs_user_detail.xml
index 91d3a53..1aec296 100644
--- a/packages/SystemUI/res/layout/qs_user_detail.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail.xml
@@ -22,6 +22,6 @@
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- sysui:verticalSpacing="4dp"
+ sysui:verticalSpacing="20dp"
sysui:horizontalSpacing="4dp"
style="@style/UserDetailView" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index cc6c5d3..3a0df28 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -24,8 +24,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="top|center_horizontal"
- android:paddingTop="16dp"
- android:minHeight="112dp"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
@@ -39,8 +37,8 @@
android:layout_height="@dimen/qs_framed_avatar_size"
android:layout_marginBottom="7dp"
systemui:frameWidth="6dp"
- systemui:badgeDiameter="18dp"
- systemui:badgeMargin="1dp"
+ systemui:badgeDiameter="15dp"
+ systemui:badgeMargin="5dp"
systemui:framePadding="-1dp"
systemui:frameColor="@color/qs_user_avatar_frame"/>
diff --git a/packages/SystemUI/res/layout/qs_user_dialog_content.xml b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
index 9495ee6..355df2c 100644
--- a/packages/SystemUI/res/layout/qs_user_dialog_content.xml
+++ b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
@@ -15,75 +15,19 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<androidx.constraintlayout.widget.ConstraintLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="24dp"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
->
- <TextView
- android:id="@+id/title"
- android:layout_height="wrap_content"
- android:layout_width="0dp"
- android:textAlignment="center"
- android:text="@string/qs_user_switch_dialog_title"
- android:textAppearance="@style/TextAppearance.QSDialog.Title"
- android:layout_marginBottom="32dp"
- sysui:layout_constraintTop_toTopOf="parent"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toEndOf="parent"
- sysui:layout_constraintBottom_toTopOf="@id/grid"
- />
-
+ >
<com.android.systemui.qs.PseudoGridView
- android:id="@+id/grid"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="28dp"
- sysui:verticalSpacing="4dp"
- sysui:horizontalSpacing="4dp"
- sysui:fixedChildWidth="80dp"
- sysui:layout_constraintTop_toBottomOf="@id/title"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toEndOf="parent"
- sysui:layout_constraintBottom_toTopOf="@id/barrier"
- />
-
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/barrier"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- sysui:barrierDirection="top"
- sysui:constraint_referenced_ids="settings,done"
+ android:id="@+id/grid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ sysui:verticalSpacing="20dp"
+ sysui:horizontalSpacing="4dp"
+ sysui:fixedChildWidth="80dp"
/>
-
- <Button
- android:id="@+id/settings"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:text="@string/quick_settings_more_user_settings"
- sysui:layout_constraintTop_toBottomOf="@id/barrier"
- sysui:layout_constraintBottom_toBottomOf="parent"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toStartOf="@id/done"
- sysui:layout_constraintHorizontal_chainStyle="spread_inside"
- style="@style/Widget.QSDialog.Button.BorderButton"
- />
-
- <Button
- android:id="@+id/done"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:text="@string/quick_settings_done"
- sysui:layout_constraintTop_toBottomOf="@id/barrier"
- sysui:layout_constraintBottom_toBottomOf="parent"
- sysui:layout_constraintStart_toEndOf="@id/settings"
- sysui:layout_constraintEnd_toEndOf="parent"
- style="@style/Widget.QSDialog.Button"
- />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index e43a149..6012b58 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -27,10 +27,10 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
- android:paddingTop="26dp"
- android:paddingBottom="30dp"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ android:paddingTop="@dimen/dialog_top_padding"
+ android:paddingBottom="@dimen/dialog_bottom_padding"
android:orientation="vertical">
<!-- Header -->
@@ -93,41 +93,6 @@
android:id="@+id/screenrecord_audio_switch"
style="@style/ScreenRecord.Switch"/>
</LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="@dimen/screenrecord_option_padding">
- <ImageView
- android:layout_width="@dimen/screenrecord_option_icon_size"
- android:layout_height="@dimen/screenrecord_option_icon_size"
- android:layout_weight="0"
- android:src="@drawable/ic_touch"
- android:tint="?android:attr/textColorSecondary"
- android:layout_gravity="center"
- android:layout_marginRight="@dimen/screenrecord_option_padding"/>
- <TextView
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:layout_weight="1"
- android:layout_gravity="fill_vertical"
- android:gravity="center_vertical"
- android:text="@string/screenrecord_taps_label"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?android:attr/textColorPrimary"
- android:importantForAccessibility="no"/>
- <Switch
- android:layout_width="wrap_content"
- android:minWidth="48dp"
- android:layout_height="48dp"
- android:layout_weight="0"
- android:id="@+id/screenrecord_taps_switch"
- android:contentDescription="@string/screenrecord_taps_label"
- style="@style/ScreenRecord.Switch"/>
- </LinearLayout>
</LinearLayout>
<!-- Buttons -->
@@ -143,10 +108,7 @@
android:layout_weight="0"
android:layout_gravity="start"
android:text="@string/cancel"
- android:textColor="?android:textColorPrimary"
- android:background="@drawable/screenrecord_button_background_outline"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="14sp"/>
+ style="@style/Widget.Dialog.Button.BorderButton" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
@@ -158,10 +120,7 @@
android:layout_weight="0"
android:layout_gravity="end"
android:text="@string/screenrecord_start"
- android:textColor="@android:color/system_neutral1_900"
- android:background="@drawable/screenrecord_button_background_solid"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="14sp"/>
+ style="@style/Widget.Dialog.Button" />
</LinearLayout>
</LinearLayout>
</ScrollView>
diff --git a/packages/SystemUI/res/layout/split_shade_header.xml b/packages/SystemUI/res/layout/split_shade_header.xml
index f2c5b7b..b6e96ce 100644
--- a/packages/SystemUI/res/layout/split_shade_header.xml
+++ b/packages/SystemUI/res/layout/split_shade_header.xml
@@ -83,6 +83,17 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
systemui:textAppearance="@style/TextAppearance.QS.Status" />
+ <FrameLayout
+ android:id="@+id/privacy_container"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:minHeight="48dp"
+ android:layout_weight="1"
+ android:paddingStart="16dp">
+
+ <include layout="@layout/ongoing_privacy_chip" />
+
+ </FrameLayout>
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 82186c1..591d8f5 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -119,9 +119,6 @@
systemui:layout_constraintEnd_toEndOf="parent"
/>
- <include layout="@layout/ambient_indication"
- android:id="@+id/ambient_indication_container" />
-
<include layout="@layout/photo_preview_overlay" />
<include
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index 332dc6ee..a2abdb2 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -26,8 +26,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="64dp"
- android:paddingTop="12dp"
android:textAppearance="?android:attr/textAppearanceButton"
- android:gravity="top|center_horizontal"
+ android:gravity="center"
android:text="@string/empty_shade_text"/>
</com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 0fa8e1b..1cf0062 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Geen toestelle beskikbaar nie"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi is nie gekoppel nie"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Keer kleure om"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleuromkering"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellings"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Gebruikerinstellings"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Vergroot die hele skerm"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Wissel"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Toeganklikheidknoppie het die toeganklikheidgebaar vervang\n\n"<annotation id="link">"Bekyk instellings"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tik om toeganklikheidkenmerke oop te maak Pasmaak of vervang knoppie in Instellings.\n\n"<annotation id="link">"Bekyk instellings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Skuif knoppie na kant om dit tydelik te versteek"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Beweeg na links bo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Beweeg na regs bo"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan programme en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporing-instellings verander. "<annotation id="link">"Verander"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Skakel vliegtuigmodus af"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kies gebruiker"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index d075d47..940e6f1 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"ምንም መሣሪያዎች አይገኙም"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi አልተገናኘም"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ብሩህነት"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ቀለማትን ግልብጥ"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ተቃራኒ ቀለም"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ተጨማሪ ቅንብሮች"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"የተጠቃሚ ቅንብሮች"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ተከናውኗል"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ሙሉ ገጽ እይታን ያጉሉ"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ማብሪያ/ማጥፊያ"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"የተደራሽነት አዝራር የተደራሽነት ምልክትን ተክቷል\n\n"<annotation id="link">" ቅንብሮችን አሳይ"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"የተደራሽነት ባህሪያትን ለመክፈት መታ ያድርጉ። ይህንን አዝራር በቅንብሮች ውስጥ ያብጁ ወይም ይተኩ።\n\n"<annotation id="link">"ቅንብሮችን አሳይ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ለጊዜው ለመደበቅ አዝራሩን ወደ ጠርዝ ያንቀሳቅሱ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ወደ ላይኛው ግራ አንቀሳቅስ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"አውታረ መረቦችን ለመቀየር፣ የኢተርኔት ግንኙነት ያቋርጡ"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"የመሣሪያ ተሞክሮን ለማሻሻል፣ መተግበሪያዎች እና አገልግሎቶች አሁንም በማንኛውም ጊዜ የWi-Fi አውታረ መረቦችን መቃኘት ይችላሉ፣ Wi-Fi ጠፍቶ ቢሆንም እንኳ። ይህንን በ Wi‑Fi ቅኝት ቅንብሮች ውስጥ መቀየር ይችላሉ። "<annotation id="link">"ቀይር"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"የአውሮፕላን ሁነታን ያጥፉ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ተጠቃሚን ይምረጡ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index e931705..61b0ced 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -221,7 +221,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"تم قفل الشاشة في الاتجاه الأفقي."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"تم قفل الشاشة في الاتجاه العمودي."</string>
<string name="dessert_case" msgid="9104973640704357717">"حالة الحلويات"</string>
- <string name="start_dreams" msgid="9131802557946276718">"شاشة التوقف"</string>
+ <string name="start_dreams" msgid="9131802557946276718">"شاشة الاستراحة"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"عدم الإزعاج"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوتوث"</string>
@@ -253,7 +253,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"لا يتوفر أي جهاز"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"لم يتم الاتصال بشبكة Wi-Fi."</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"السطوع"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"قلب الألوان"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"قلب الألوان"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"المزيد من الإعدادات"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"إعدادات المستخدم"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"تم"</string>
@@ -763,7 +763,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"تكبير الشاشة كلها"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"تبديل"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"تم استبدال \"زر أدوات تسهيل الاستخدام\" بإيماءة تسهيل الاستخدام.\n\n"<annotation id="link">"الاطّلاع على الإعدادات"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"انقر لفتح ميزات تسهيل الاستخدام. يمكنك تخصيص هذا الزر أو استبداله من الإعدادات.\n\n"<annotation id="link">"عرض الإعدادات"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"يمكنك نقل الزر إلى الحافة لإخفائه مؤقتًا."</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"نقل إلى أعلى يمين الشاشة"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"نقل إلى أعلى يسار الشاشة"</string>
@@ -901,5 +901,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"للتبديل بين الشبكات، يجب فصل إيثرنت."</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"لتحسين تجربتك على الجهاز، يظل بإمكان التطبيقات والخدمات البحث عن شبكات Wi‑Fi في أي وقت، حتى عند إيقاف شبكة Wi‑Fi. وبإمكانك تغيير هذا الخيار في إعدادات البحث عن شبكات Wi-Fi. "<annotation id="link">"تغيير"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"إيقاف وضع الطيران"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"اختيار المستخدم"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 4dcad91..e98682c 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"কোনো ডিভাইচ নাই"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ৱাই-ফাইৰ সৈতে সংযোগ হৈ থকা নাই"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ৰং ওলোটা কৰক"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ৰং বিপৰীতকৰণ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"অধিক ছেটিং"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ব্যৱহাৰকাৰীৰ ছেটিং"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন কৰা হ’ল"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"পূৰ্ণ স্ক্ৰীন বিবৰ্ধন কৰক"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ছুইচ"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"সাধ্য সুবিধাৰ বুটামটোৱে সাধ্য সুবিধাৰ নিৰ্দেশ সলনি কৰিছে\n\n"<annotation id="link">"ছেটিং চাওক"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"সাধ্য সুবিধাসমূহ খুলিবলৈ টিপক। ছেটিঙত এই বুটামটো কাষ্টমাইজ অথবা সলনি কৰক।\n\n"<annotation id="link">"ছেটিং চাওক"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"বুটামটোক সাময়িকভাৱে লুকুৱাবলৈ ইয়াক একেবাৰে কাষলৈ লৈ যাওক"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"শীৰ্ষৰ বাওঁফালে নিয়ক"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটৱৰ্ক সলনি কৰিবলৈ ইথাৰনেটৰ পৰা সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ডিভাইচ ব্যৱহাৰৰ অভিজ্ঞতা উন্নত কৰিবলৈ ৱাই-ফাই অফ থকা অৱস্থাতো এপ্ আৰু সেৱাসমূহে ৱাই-ফাই নেটৱৰ্কবোৰ স্কেন কৰিব পাৰে। আপুনি ৱাই-ফাই স্কেনিঙৰ ছেটিঙত এইটো সলনি কৰিব পাৰে। "<annotation id="link">"সলনি কৰক"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"এয়াৰপ্লে’ন ম’ডটো অফ কৰক"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যৱহাৰকাৰী বাছনি কৰক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index bf2e9a4..6f963f7 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Heç bir cihaz əlçatan deyil"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi qoşulu deyil"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaqlıq"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Rəng inversiyası"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rəng inversiyası"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Digər ayarlar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"İstifadəçi ayarları"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hazır"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekranı böyüdün"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran hissəsinin böyüdülməsi"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Dəyişdirici"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Jest xüsusi imkanlar düyməsinə dəyişdirildi\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Əlçatımlılıq funksiyalarını açmaq üçün toxunun. Ayarlarda bu düyməni fərdiləşdirin və ya dəyişdirin.\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düyməni müvəqqəti gizlətmək üçün kənara çəkin"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuxarıya sola köçürün"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuxarıya sağa köçürün"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Şəbəkəni dəyişmək üçün etherneti ayırın"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Cihaz təcrübəsini yaxşılaşdırmaq üçün Wi-Fi deaktiv olduqda belə, tətbiqlər və xidmətlər Wi-Fi şəbəkəsini axtara biləcək. Bunu Wi-Fi axtarışı ayarlarında dəyişə bilərsiniz. "<annotation id="link">"Dəyişin"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Təyyarə rejimini deaktiv edin"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"İstifadəçi seçin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 5d582b1..4a960ba 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -250,7 +250,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nije dostupan nijedan uređaj"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi nije povezan"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvetljenost"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Obrni boje"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Još podešavanja"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisnička podešavanja"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -669,7 +669,7 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Neke funkcije su ograničene dok se telefon ne ohladi.\nDodirnite za više informacija"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon će automatski pokušati da se ohladi. I dalje ćete moći da koristite telefon, ali će sporije reagovati.\n\nKada se telefon ohladi, normalno će raditi."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pogledajte upozorenja"</string>
- <string name="high_temp_alarm_title" msgid="2359958549570161495">"Isključite punjač iz napajanja"</string>
+ <string name="high_temp_alarm_title" msgid="2359958549570161495">"Isključite punjač iz struje"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Došlo je do problema sa punjenjem ovog uređaja. Isključite adapter iz napajanja i budite pažljivi jer kabl može da bude topao."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Pogledajte upozorenja"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Leva prečica"</string>
@@ -748,7 +748,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Uvećajte ceo ekran"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećajte deo ekrana"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pređi"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Dugme Pristupačnost je zamenilo pokret za pristupačnost\n\n"<annotation id="link">"Prikaži podešavanja"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite za funkcije pristupačnosti. Prilagodite ili zamenite ovo dugme u Podešavanjima.\n\n"<annotation id="link">"Podešavanja"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomerite dugme do ivice da biste ga privremeno sakrili"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premesti gore levo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premesti gore desno"</string>
@@ -883,5 +883,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste promenili mrežu, prekinite eternet vezu"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi boljeg doživljaja uređaja, aplikacije i usluge i dalje mogu da traže WiFi mreže u bilo kom trenutku, čak i kada je WiFi isključen. To možete da promenite u podešavanjima WiFi skeniranja. "<annotation id="link">"Promenite"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Isključite režim rada u avionu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izaberite korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 3b7c3bf..fc3e52f 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Няма даступных прылад"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Няма падключэння да Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркасць"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Інвертаваць колеры"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія колераў"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Дадатковыя налады"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Налады карыстальніка"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Гатова"</string>
@@ -753,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Павялічыць увесь экран"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пераключальнік"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Замест жэста спецыяльных магчымасцей будзе выкарыстоўвацца кнопка\n\n"<annotation id="link">"Праглядзець налады"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Націсніце, каб адкрыць спецыяльныя магчымасці. Рэгулюйце ці замяняйце кнопку ў Наладах.\n\n"<annotation id="link">"Прагляд налад"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Каб часова схаваць кнопку, перамясціце яе на край"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перамясціць лявей і вышэй"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перамясціць правей і вышэй"</string>
@@ -889,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Каб падключыцца да сетак, выключыце Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Каб палепшыць працу прылады, вы можаце дазволіць праграмам і сэрвісам шукаць сеткі Wi-Fi, нават калі Wi‑Fi выключаны. Змяніць гэты рэжым можна ў наладах пошуку сетак Wi-Fi. "<annotation id="link">"Змяніць"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Выключыць рэжым палёту"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выбар карыстальніка"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 1f57aa4..39317a9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Няма налични устройства"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"не е установена връзка с Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркост"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Инвертиране на цветовете"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инвертиране на цветовете"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Още настройки"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Потребителски настройки"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увеличаване на целия екран"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличаване на част от екрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Превключване"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Жестът за достъпност бе заменен от бутон\n\n"<annotation id="link">"Преглед на настройките"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Докоснете, за да отворите функциите за достъпност. Персон./заменете бутона от настройките.\n\n"<annotation id="link">"Преглед на настройките"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете бутона до края, за да го скриете временно"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Преместване горе вляво"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Преместване горе вдясно"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За да превключите мрежите, прекъснете връзката с Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"С цел подобряване на практическата работа с устройството приложенията и услугите пак могат да сканират за Wi‑Fi мрежи по всяко време дори когато функцията за Wi‑Fi e изключена. Можете да промените съответното поведение от настройките за сканиране за Wi‑Fi. "<annotation id="link">"Промяна"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Изключване на самолетния режим"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Избор на потребител"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 8f891ee..0c61dda 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"কোনো ডিভাইস উপলব্ধ নয়"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ওয়াই-ফাই কানেক্ট করা নেই"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"বিপরীত রঙ"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"কালার ইনভার্সন"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"আরও সেটিংস"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ব্যবহারকারী সেটিংস"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন হয়েছে"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"সম্পূর্ণ স্ক্রিন বড় করে দেখা"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"বদল করুন"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"অ্যাক্সেসিবিলিটি জেসচার পরিবর্তন করে অ্যাক্সেসেবিলিটি বোতাম করা হয়েছে\n\n"<annotation id="link">"সেটিংস দেখুন"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"অ্যাক্সেসিবিলিটি ফিচার খুলতে ট্যাপ করুন। কাস্টমাইজ করুন বা সেটিংসে এই বোতামটি সরিয়ে দিন।\n\n"<annotation id="link">"সেটিংস দেখুন"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"এটি অস্থায়ীভাবে লুকাতে বোতামটি কোণে সরান"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"উপরে বাঁদিকে সরান"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"উপরে ডানদিকে সরান"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটওয়ার্ক বদলাতে ইথারনেট ডিসকানেক্ট করুন"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ডিভাইস সংক্রান্ত অভিজ্ঞতা আরও ভাল করতে, অ্যাপ ও পরিষেবা যেকোনও সময় আপনার ওয়াই-ফাই নেটওয়ার্ক স্ক্যান করতে পারবে, এমনকি ডিভাইসের ওয়াই-ফাই বন্ধ করা থাকলেও। ওয়াই-ফাই স্ক্যানিং সেটিংস থেকে আপনি এটি পরিবর্তন করতে পারবেন। "<annotation id="link">"পরিবর্তন করুন"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"\'বিমান\' মোড বন্ধ করুন"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যবহারকারী বেছে নিন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index abb686b..510161c 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -250,7 +250,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nema dostupnih uređaja"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi mreža nije povezana"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvjetljenje"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverzija boja"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Više postavki"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisničke postavke"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -748,7 +748,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Uvećavanje prikaza preko cijelog ekrana"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećavanje dijela ekrana"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prekidač"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Dugme za pristupačnost je zamijenilo pokret za pristupačnost\n\n"<annotation id="link">"Prikaži postavke"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite da otvorite funkcije pristupačnosti. Prilagodite ili zamijenite dugme u Postavkama.\n\n"<annotation id="link">"Postavke"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Premjestite dugme do ivice da ga privremeno sakrijete"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pomjeranje gore lijevo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pomjeranje gore desno"</string>
@@ -883,5 +883,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu bilo kada skenirati WiFi mreže, čak i kada je WiFi isključen. Ovo možete promijeniti u Postavkama skeniranja WiFi mreže. "<annotation id="link">"Promijeni"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Isključi način rada u avionu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odaberite korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 6903d17..0592283 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No hi ha cap dispositiu disponible."</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"La Wi‑Fi no està connectada"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix colors"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversió de colors"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Més opcions"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuració d\'usuari"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Amplia la pantalla completa"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Canvia"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El gest d\'accessibilitat s\'ha substituït pel botó d\'accessibilitat\n\n"<annotation id="link">"Mostra la configuració"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca per obrir funcions d\'accessibilitat. Personalitza o substitueix el botó a Configuració.\n\n"<annotation id="link">"Mostra"</annotation>"."</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mou el botó a l\'extrem per amagar-lo temporalment"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mou a dalt a l\'esquerra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mou a dalt a la dreta"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per canviar de xarxa, desconnecta la connexió Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Per millorar l\'experiència del dispositiu, les aplicacions i els serveis poden cercar xarxes Wi‑Fi en qualsevol moment, fins i tot quan la Wi‑Fi estigui desactivada. Pots canviar aquesta opció a la configuració de cerca de xarxes Wi‑Fi. "<annotation id="link">"Canvia-la"</annotation>"."</string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Desactiva el mode d\'avió"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuari"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 19533fb..a6ec1f0 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nejsou dostupná žádná zařízení"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Není připojena Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Převrátit barvy"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Převrácení barev"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Další nastavení"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Uživatelské nastavení"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
@@ -753,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zvětšit celou obrazovku"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zvětšit část obrazovky"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Přepnout"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tlačítko přístupnosti bylo nahrazeno gestem přístupnosti\n\n"<annotation id="link">"Zobrazit nastavení"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Klepnutím otevřete funkce přístupnosti. Tlačítko lze upravit nebo nahradit v Nastavení.\n\n"<annotation id="link">"Nastavení"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Přesunutím tlačítka k okraji ho dočasně skryjete"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Přesunout vlevo nahoru"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Přesunout vpravo nahoru"</string>
@@ -889,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pokud chcete přepnout sítě, odpojte ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Za účelem lepšího fungování zařízení mohou aplikace a služby vyhledávat sítě Wi-Fi, i když je připojení Wi-Fi vypnuté. Toto chování můžete změnit v nastavení vyhledávání Wi-Fi. "<annotation id="link">"Změnit"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Vypnout režim Letadlo"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zvolte uživatele"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 470714d..9d33636 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Der er ingen tilgængelige enheder"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Manglende Wi-Fi-forbindelse"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Ombyt farver"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ombytning af farver"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Flere indstillinger"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Brugerindstillinger"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Udfør"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Forstør hele skærmen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Skift"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Knappen Hjælpefunktioner har erstattet bevægelsen for hjælpefunktioner\n\n"<annotation id="link">"Se indstillinger"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tryk for at åbne hjælpefunktioner. Tilpas eller erstat denne knap i Indstillinger.\n\n"<annotation id="link">"Se indstillingerne"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flyt knappen til kanten for at skjule den midlertidigt"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flyt op til venstre"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flyt op til højre"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Afbryd ethernetforbindelsen for at skifte netværk"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"For at forbedre brugeroplevelsen på enheden kan apps og tjenester stadig til enhver tid scanne efter Wi‑Fi-netværk, også selvom Wi‑Fi er deaktiveret. Du kan ændre dette i indstillingerne for Wi-Fi-scanning. "<annotation id="link">"Skift indstilling"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Deaktiver flytilstand"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vælg bruger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 85bb365..22c7cb3 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -180,7 +180,7 @@
<string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"Der Flugmodus ist aktiviert."</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"lautlos"</string>
<string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"nur Weckrufe"</string>
- <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Nicht stören."</string>
+ <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Bitte nicht stören."</string>
<string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"„Bitte nicht stören“ deaktiviert."</string>
<string name="accessibility_quick_settings_dnd_changed_on" msgid="186315911607486129">"„Bitte nicht stören“ aktiviert"</string>
<string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Keine Geräte verfügbar"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WLAN nicht verbunden"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helligkeit"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Farben umkehren"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Farbumkehr"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Weitere Einstellungen"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Nutzereinstellungen"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fertig"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ganzen Bildschirm vergrößern"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schalter"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Die Schaltfläche „Bedienungshilfen“ ersetzt die Touch-Geste für Bedienungshilfen\n\n"<annotation id="link">"Einstellungen aufrufen"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tippe, um die Bedienungshilfen aufzurufen. Du kannst diese Schaltfläche in den Einstellungen anpassen oder ersetzen.\n\n"<annotation id="link">"Zu den Einstellungen"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Durch Ziehen an den Rand wird die Schaltfläche zeitweise ausgeblendet"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Nach oben links verschieben"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Nach rechts oben verschieben"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Trenne das Ethernetkabel, um das Netzwerk zu wechseln"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Zur Verbesserung der Gerätenutzung können Apps und Dienste weiter nach WLANs suchen, auch wenn die WLAN-Funktion deaktiviert ist. Dies lässt sich in den Einstellungen für die WLAN-Suche ändern. "<annotation id="link">"Ändern"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Flugmodus deaktivieren"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Nutzer auswählen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index d857e4a..3af4cc5 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Δεν υπάρχουν διαθέσιμες συσκευές"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Το Wi-Fi δεν είναι συνδεδεμένο"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Φωτεινότητα"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Αντιστροφή χρωμάτων"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Αντιστροφή χρωμάτων"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Περισσότερες ρυθμίσεις"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ρυθμίσεις χρήστη"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Τέλος"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Μεγέθυνση πλήρους οθόνης"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Εναλλαγή"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Το κουμπί προσβασιμότητας αντικατέστησε την κίνηση προσβασιμότητας\n\n"<annotation id="link">"Προβολή ρυθμίσεων"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Πατήστε για άνοιγμα των λειτουργιών προσβασιμότητας. Προσαρμόστε ή αντικαταστήστε το κουμπί στις Ρυθμίσεις.\n\n"<annotation id="link">"Προβολή ρυθμίσεων"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Μετακινήστε το κουμπί στο άκρο για προσωρινή απόκρυψη"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Μετακίνηση επάνω αριστερά"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Μετακίνηση επάνω δεξιά"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Για εναλλαγή δικτύων, αποσυνδέστε το ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Για βελτίωση της εμπειρίας στη συσκευή, οι εφαρμογές και οι υπηρεσίες μπορούν ακόμα να εκτελούν σάρωση για δίκτυα Wi‑Fi ανά πάσα στιγμή, ακόμα και όταν το Wi‑Fi είναι απενεργοποιημένο. Μπορείτε να αλλάξετε αυτήν τη ρύθμιση στις ρυθμίσεις της Σάρωσης Wi‑Fi. "<annotation id="link">"Αλλαγή"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Απενεργοποίηση λειτουργίας πτήσης"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Επιλογή χρήστη"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index fd27f71..ffff2a9 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No devices available"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invert colours"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Turn off aeroplane mode"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 52e41e5..f81f931 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No devices available"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invert colours"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Turn off aeroplane mode"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index fd27f71..ffff2a9 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No devices available"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invert colours"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Turn off aeroplane mode"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index fd27f71..ffff2a9 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No devices available"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invert colours"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Turn off aeroplane mode"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index c54d610..620f995 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No devices available"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi not connected"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invert colors"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Color inversion"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"More settings"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation>""</string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customize or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation>""</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation>""</string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Turn off airplane mode"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 823e11d..b47616c 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No hay dispositivos disponibles"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Red Wi-Fi no conectada"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invertir colores"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión de color"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Más configuraciones"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuración del usuario"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Listo"</string>
@@ -739,11 +739,11 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string>
- <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Botón de ampliación"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor de ampliación"</string>
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
- <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botón"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El botón de accesibilidad ha reemplazado el gesto de accesibilidad\n\n"<annotation id="link">"Ver configuración"</annotation></string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Interruptor"</string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Presiona para abrir las funciones de accesibilidad. Personaliza o cambia botón en Config.\n\n"<annotation id="link">"Ver config"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconéctate de Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para mejorar la experiencia con el dispositivo, las apps y los servicios pueden seguir buscando redes Wi-Fi en cualquier momento, incluso cuando la conexión Wi-Fi esté desactivada. Puedes cambiar este parámetro en la configuración de búsqueda de Wi-Fi. "<annotation id="link">"Cambiar"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Desactivar el modo de avión"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 420d4ac..61f4306 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4811759950673118541">"UI del sistema"</string>
- <string name="battery_low_title" msgid="6891106956328275225">"Es posible que te quedes sin batería pronto"</string>
+ <string name="battery_low_title" msgid="6891106956328275225">"Puede que te quedes sin batería pronto"</string>
<string name="battery_low_percent_format" msgid="4276661262843170964">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="invalid_charger_title" msgid="938685362320735167">"No se puede cargar por USB"</string>
<string name="invalid_charger_text" msgid="2339310107232691577">"Utiliza el cargador original incluido con el dispositivo"</string>
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"No hay dispositivos disponibles"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi no conectado"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invertir colores"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Más ajustes"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ajustes de usuario"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hecho"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El botón Accesibilidad ha reemplazado el gesto de accesibilidad\n\nVer ajustes"<annotation id="link"></annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca para abrir funciones de accesibilidad. Personaliza o sustituye este botón en Ajustes.\n\n"<annotation id="link">"Ver ajustes"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconecta el cable Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para mejorar la experiencia con el dispositivo, las aplicaciones y los servicios podrán buscar redes Wi-Fi en cualquier momento, aunque la conexión Wi-Fi esté desactivada. Puedes cambiarlo en los ajustes de búsqueda de redes Wi-Fi. "<annotation id="link">"Cambiar"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Desactivar modo avión"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 6e49ca1..11961db 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Ühtegi seadet pole saadaval"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi-ühendus puudub"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Heledus"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Värvide vahetamine"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Värvide ümberpööramine"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Rohkem seadeid"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Kasutaja seaded"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Täisekraani suurendamine"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaheta"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Juurdepääsetavuse nupp asendas juurdepääsuliigutuse\n\n"<annotation id="link">"Vaadake seadeid"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Puudutage juurdepääsufunktsioonide avamiseks. Kohandage nuppu või asendage see seadetes.\n\n"<annotation id="link">"Kuva seaded"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Teisaldage nupp serva, et see ajutiselt peita"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Teisalda üles vasakule"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Teisalda üles paremale"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Võrkude vahetamiseks katkestage Etherneti-ühendus"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Seadme kasutuskogemuse parandamiseks võivad rakendused ja teenused siiski alati otsida WiFi-võrke isegi siis, kui WiFi on väljas. Seda saab muuta WiFi-skannimise seadetes. "<annotation id="link">"Muuda"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Lennureżiimi väljalülitamine"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kasutaja valimine"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 824e5b3..e82e8f5 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Ez dago gailurik erabilgarri"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Ez zaude konektatuta wifi-sarera"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Distira"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Alderantzikatu koloreak"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kolore-alderantzikatzea"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Ezarpen gehiago"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Erabiltzaile-ezarpenak"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Eginda"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Handitu pantaila osoa"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botoia"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Erabilerraztasuna botoiak erabilerraztasun-keinua ordezkatu du\n\n"<annotation id="link">"Ikusi ezarpenak"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Erabilerraztasun-eginbideak irekitzeko, sakatu hau. Ezarpenetan pertsonalizatu edo ordez dezakezu botoia.\n\n"<annotation id="link">"Ikusi ezarpenak"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Eraman botoia ertzera aldi baterako ezkutatzeko"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Eraman goialdera, ezkerretara"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Eraman goialdera, eskuinetara"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Sarea aldatzeko, deskonektatu Ethernet-a"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Gailuaren funtzionamendua hobetzeko, aplikazioek eta zerbitzuek wifi-sareak bilatzen jarraituko dute, baita wifi-konexioa desaktibatuta dagoenean ere. Aukera hori aldatzeko, joan wifi-sareen bilaketaren ezarpenetara. "<annotation id="link">"Aldatu"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Desaktibatu hegaldi modua"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Hautatu erabiltzaile bat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e6929b8..c4c676f 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"دستگاهی موجود نیست"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi وصل نیست"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"روشنایی"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"برگردان رنگها"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"وارونگی رنگ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"تنظیمات بیشتر"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"تنظیمات کاربر"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"تمام"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"درشتنمایی تمامصفحه"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشتنمایی بخشی از صفحه"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"کلید"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"دکمه دسترسپذیری جایگزین اشاره دسترسپذیری شد\n\n"<annotation id="link">"مشاهده تنظیمات"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"برای باز کردن ویژگیهای دسترسپذیری ضربه بزنید. در تنظیمات این دکمه را سفارشی یا جایگزین کنید\n\n"<annotation id="link">"تنظیمات"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"برای پنهان کردن موقتی دکمه، آن را به لبه ببرید"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"انتقال به بالا سمت راست"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"انتقال به بالا سمت چپ"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"برای تغییر شبکه، اترنت را قطع کنید"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"برای بهبود تجربه استفاده از دستگاه، برنامهها و سرویسها همچنان میتوانند در هر زمانی شبکههای Wi-Fi را اسکن کنند؛ حتی وقتی که Wi-Fi خاموش باشد. میتوانید این مورد را در تنظیمات اسکن کردن Wi‑Fi تغییر دهید. "<annotation id="link">"تغییر"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"خاموش کردن حالت هواپیما"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"انتخاب کاربر"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index a62dcda..d9b7291 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Laitteita ei ole käytettävissä"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fiä ei ole yhdistetty"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kirkkaus"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Käänteiset värit"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Käänteiset värit"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Lisäasetukset"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Käyttäjäasetukset"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Koko näytön suurennus"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaihda"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Esteettömyyspainike on korvannut esteettömyyseleen\n\n"<annotation id="link">"Katso asetukset"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Avaa esteettömyysominaisuudet napauttamalla. Yksilöi tai vaihda painike asetuksista.\n\n"<annotation id="link">"Avaa asetukset"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Piilota painike tilapäisesti siirtämällä se reunaan"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Siirrä vasempaan yläreunaan"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Siirrä oikeaan yläreunaan"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Irrota Ethernet-johto, jos haluat vaihtaa verkkoa"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Laitteen käyttökokemuksen parantamiseksi sovellukset ja palvelut voivat hakea Wi-Fi-verkkoja myös silloin, kun Wi-Fi on pois päältä. Voit muuttaa asetusta Wi-Fi-haun asetuksissa. "<annotation id="link">"Muuta"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Laita lentokonetila pois päältä"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Valitse käyttäjä"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index e8fae53..9f1fee9 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Aucun appareil à proximité"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Non connecté au Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverser les couleurs"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Plus de paramètres"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Paramètres utilisateur"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Terminé"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Agrandir la totalité de l\'écran"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Commutateur"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Le bouton d\'accessibilité a remplacé le geste d\'accessibilité\n\n"<annotation id="link">"Voir les paramètres"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Touchez pour ouvrir fonction. d\'access. Personnalisez ou remplacez bouton dans Param.\n\n"<annotation id="link">"Afficher param."</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacez le bouton vers le bord pour le masquer temporairement"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer dans coin sup. gauche"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer dans coin sup. droit"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, débranchez le câble Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pour améliorer l\'expérience de l\'appareil, les applications et les services peuvent quand même rechercher des réseaux Wi-Fi en tout temps, même lorsque le Wi-Fi est désactivé. Vous pouvez modifier vos préférences dans les paramètres de recherche de réseaux Wi-Fi. "<annotation id="link">"Modifier"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Désactiver le mode Avion"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Sélect. utilisateur"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 2631a42..8110149 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Aucun appareil disponible."</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi non connecté"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverser les couleurs"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Plus de paramètres"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Paramètres utilisateur"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"OK"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Agrandir tout l\'écran"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Changer"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Le bouton Accessibilité a remplacé le geste d\'accessibilité\n\n"<annotation id="link">"Afficher les paramètres"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Appuyez pour ouvrir fonctionnalités d\'accessibilité. Personnalisez ou remplacez bouton dans paramètres.\n\n"<annotation id="link">"Voir paramètres"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacer le bouton vers le bord pour le masquer temporairement"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer en haut à gauche"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer en haut à droite"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, déconnectez l\'Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pour améliorer l\'expérience sur l\'appareil, les applis et les services peuvent continuer de rechercher les réseaux Wi-Fi, même si le Wi-Fi est désactivé. Vous pouvez modifier cela dans les paramètres de recherche Wi-Fi. "<annotation id="link">"Modifier"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Désactiver le mode Avion"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir utilisateur"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9d51368..724435c 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Non hai dispositivos dispoñibles"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"A wifi non está conectada"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverter cores"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión da cor"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Máis opcións"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuración de usuario"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Feito"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botón de accesibilidade substituíu o xesto de accesibilidade\n\n"<annotation id="link">"Ver configuración"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca para abrir as funcións de accesibilidade. Cambia este botón en Configuración.\n\n"<annotation id="link">"Ver configuración"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Para ocultar temporalmente o botón, móveo ata o bordo"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover á parte super. esquerda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover á parte superior dereita"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de rede, desconecta a Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para mellorar a experiencia que ofrece o dispositivo, as aplicacións e os servizos poden seguir buscando redes wifi en calquera momento, aínda que esta conexión estea desactivada. Podes cambiar esta opción na configuración da función Busca de redes wifi. "<annotation id="link">"Cambiar"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Desactivar modo avión"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 3f287e6..eefe252 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"કોઈ ઉપકરણો ઉપલબ્ધ નથી"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"વાઇ-ફાઇ કનેક્ટ નથી"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"તેજ"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"રંગોને ઉલટાવો"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"વિપરીત રંગમાં બદલવું"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"વધુ સેટિંગ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"વપરાશકર્તા સેટિંગ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"થઈ ગયું"</string>
@@ -332,7 +332,7 @@
<string name="user_new_user_name" msgid="2019166282704195789">"નવો વપરાશકર્તા"</string>
<string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"અતિથિ દૂર કરીએ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"દૂર કરો"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"કાઢી નાખો"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ફરી સ્વાગત છે, અતિથિ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ રાખવા માંગો છો?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"પૂર્ણ સ્ક્રીનને મોટી કરો"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"સ્વિચ"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ઍક્સેસિબિલિટી સંકેતને ઍક્સેસિબિલિટી બટન વડે બદલવામાં આવ્યા છે\n\n"<annotation id="link">"સેટિંગ જુઓ"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ઍક્સેસિબિલિટી સુવિધાઓ ખોલવા માટે ટૅપ કરો. સેટિંગમાં આ બટનને કસ્ટમાઇઝ કરો અથવા બદલો.\n\n"<annotation id="link">"સેટિંગ જુઓ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"તેને હંગામી રૂપે ખસેડવા માટે બટનને કિનારી પર ખસેડો"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ઉપર ડાબે ખસેડો"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ઉપર જમણે ખસેડો"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"બીજા નેટવર્ક પર જવા માટે, ઇથરનેટ ડિસ્કનેક્ટ કરો"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ડિવાઇસના અનુભવને બહેતર બનાવવા માટે, વાઇ-ફાઇ બંધ હોય ત્યારે પણ ઍપ અને સેવાઓ કોઈપણ સમયે વાઇ-ફાઇ નેટવર્ક સ્કૅન કરી શકે છે. તમે વાઇ-ફાઇ સ્કૅનિંગના સેટિંગમાં જઈને આને બદલી શકો છો. "<annotation id="link">"બદલો"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"એરપ્લેન મોડ બંધ કરો"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"વપરાશકર્તા પસંદ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index f057603..1d6f279 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -22,5 +22,5 @@
<dimen name="large_clock_text_size">200dp</dimen>
<!-- With the large clock, move up slightly from the center -->
- <dimen name="keyguard_large_clock_top_margin">-104dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index c5ea217..f267f6f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"कोई डिवाइस उपलब्ध नहीं"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"वाई-फ़ाई कनेक्ट नहीं है"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"स्क्रीन की रोशनी"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"रंग उलटें"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"रंग बदलने की सुविधा"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"और सेटिंग"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"उपयोगकर्ता सेटिंग"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"हो गया"</string>
@@ -685,7 +685,7 @@
<string name="notification_channel_battery" msgid="9219995638046695106">"बैटरी"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"स्क्रीनशॉट"</string>
<string name="notification_channel_general" msgid="4384774889645929705">"सामान्य संदेश"</string>
- <string name="notification_channel_storage" msgid="2720725707628094977">"जगह"</string>
+ <string name="notification_channel_storage" msgid="2720725707628094977">"स्टोरेज"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"संकेत"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
<string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> चल रहा है"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फ़ुल स्क्रीन को ज़ूम करें"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"सुलभता वाले हाथ के जेस्चर (हाव-भाव) को सुलभता बटन से बदल दिया गया है\n\n"<annotation id="link">"सेटिंग देखें"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"सुलभता सुविधाएं खोलने के लिए टैप करें. सेटिंग में, इस बटन को बदलें या अपने हिसाब से सेट करें.\n\n"<annotation id="link">"सेटिंग देखें"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटन को कुछ समय छिपाने के लिए, उसे किनारे पर ले जाएं"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सबसे ऊपर बाईं ओर ले जाएं"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सबसे ऊपर दाईं ओर ले जाएं"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदलने के लिए, पहले ईथरनेट को डिसकनेक्ट करें"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"डिवाइस इस्तेमाल करने के अनुभव काे बेहतर बनाने के लिए, ऐप्लिकेशन और सेवाओं की मदद से, किसी भी समय वाई-फ़ाई नेटवर्क स्कैन किए जा सकते हैं. ऐसा वाई-फ़ाई बंद होने पर भी किया जा सकता है. वाई-फ़ाई स्कैनिंग की सेटिंग में जाकर, इसे बदला जा सकता है. "<annotation id="link">"बदलें"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"हवाई जहाज़ मोड बंद करें"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"उपयोगकर्ता चुनें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index e557f44..f0062a6 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -250,7 +250,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nema dostupnih uređaja"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi mreža nije povezana"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svjetlina"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Zamjena boja"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Više postavki"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisničke postavke"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -748,7 +748,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Povećajte cijeli zaslon"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebacivanje"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Gumb za Pristupačnost zamijenio je pokret pristupačnosti\n\n"<annotation id="link">"Prikaz postavki"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite za otvaranje značajki pristupačnosti. Prilagodite ili zamijenite taj gumb u postavkama.\n\n"<annotation id="link">"Pregledajte postavke"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomaknite gumb do ruba da biste ga privremeno sakrili"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premjesti u gornji lijevi kut"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premjesti u gornji desni kut"</string>
@@ -883,5 +883,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste se prebacili na drugu mrežu, odspojite Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Da bi se poboljšao doživljaj uređaja, aplikacije i usluge i dalje mogu tražiti Wi-Fi mreže u bilo kojem trenutku, čak i kada je Wi-Fi isključen. To možete promijeniti u postavkama traženja Wi-Fija. "<annotation id="link">"Promijeni"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Isključi način rada u zrakoplovu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odabir korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 623cc38..28523b7 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nem áll rendelkezésre eszköz"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nem kapcsolódik Wi‑Fi-hálózathoz"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Fényerő"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Színek invertálása"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Színek invertálása"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"További beállítások"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Felhasználói beállítások"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Kész"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"A teljes képernyő felnagyítása"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Váltás"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"A kisegítő kézmozdulat helyébe a Kisegítő lehetőségek gomb lépett\n\n"<annotation id="link">"Beállítások megtekintése"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Koppintson a kisegítő lehetőségek megnyitásához. A gombot a Beállításokban módosíthatja.\n\n"<annotation id="link">"Beállítások"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"A gombot a szélre áthelyezve ideiglenesen elrejtheti"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Áthelyezés fel és balra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Áthelyezés fel és jobbra"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Hálózatváltáshoz válassza le az ethernetet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Az eszközhasználati élmény javítása érdekében az alkalmazások és a szolgáltatások továbbra is bármikor kereshetnek Wi-Fi-hálózatokat, még akkor is, ha a Wi-Fi ki van kapcsolva. A funkciót a „Wi-Fi scanning settings” (Wi-Fi-keresési beállítások) részben módosíthatja. "<annotation id="link">"Módosítás"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Repülős üzemmód kikapcsolása"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Felhasználóválasztás"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index a57aaa8..d9c233a 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Հասանելի սարքեր չկան"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi-ը միացված չէ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Պայծառություն"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Շրջել գույները"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Գունաշրջում"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Հավելյալ կարգավորումներ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Օգտատիրոջ կարգավորումներ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Պատրաստ է"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Խոշորացնել ամբողջ էկրանը"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Փոխել"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Հատուկ գործառույթների ժեստը փոխարինվել է կոճակով\n\n"<annotation id="link">"Բացել կարգավորումները"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Հատուկ գործառույթները բացելու համար հպեք։ Անհատականացրեք այս կոճակը կարգավորումներում։\n\n"<annotation id="link">"Կարգավորումներ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Կոճակը ժամանակավորապես թաքցնելու համար այն տեղափոխեք էկրանի եզր"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Տեղափոխել վերև՝ ձախ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Տեղափոխել վերև՝ աջ"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Մի ցանցից մյուսին անցնելու համար անջատեք Ethernet-ը"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Սարքի աշխատանքը բարելավելու համար հավելվածներն ու ծառայությունները կորոնեն Wi‑Fi ցանցեր, նույնիսկ երբ Wi‑Fi-ն անջատված է։ Այս պարամետրը կարող եք փոխել Wi‑Fi ցանցերի որոնման կարգավորումներում։ "<annotation id="link">"Փոխել"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Անջատել ավիառեժիմը"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Ընտրեք օգտատեր"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 48d5bb1..13b79ef 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Edit"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Mengedit screenshot"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Bagikan screenshot"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Ambil lebih banyak"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Menutup screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string>
@@ -250,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Perangkat tak tersedia"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi tidak terhubung"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inversi warna"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversi warna"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Setelan lainnya"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setelan pengguna"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
@@ -744,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Memperbesar tampilan layar penuh"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Alihkan"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tombol aksesibilitas menggantikan gestur aksesibilitas\n\n"<annotation id="link">"Tampilkan setelan"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ketuk untuk membuka fitur aksesibilitas. Sesuaikan atau ganti tombol ini di Setelan.\n\n"<annotation id="link">"Lihat setelan"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pindahkan tombol ke tepi agar tersembunyi untuk sementara"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pindahkan ke kiri atas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pindahkan ke kanan atas"</string>
@@ -878,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk beralih jaringan, lepaskan kabel ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Agar pengalaman perangkat menjadi lebih baik, aplikasi dan layanan tetap dapat memindai jaringan Wi-Fi kapan saja, bahkan saat Wi-Fi nonaktif. Anda dapat mengubahnya di setelan pemindaian Wi-Fi. "<annotation id="link">"Ubah"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Menonaktifkan mode pesawat"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 4d09cd8..8bbd76d 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Engin tæki til staðar"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi ekki tengt"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Birtustig"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Umsnúa litum"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Umsnúningur lita"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Fleiri stillingar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Notandastillingar"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Lokið"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Stækka allan skjáinn"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Rofi"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Aðgengishnappur kom í stað aðgengisbendingar\n\n"<annotation id="link">"Skoða stillingar"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ýttu til að opna aðgengiseiginleika. Sérsníddu eða skiptu hnappinum út í stillingum.\n\n"<annotation id="link">"Skoða stillingar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Færðu hnappinn að brúninni til að fela hann tímabundið"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Færa efst til vinstri"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Færa efst til hægri"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aftengdu ethernet til að skipta um net"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Til að bæta tækjaupplifun geta forrit og þjónustur áfram leitað að WiFi-netum hvenær sem er, jafnvel þótt slökkt sé á WiFi. Hægt er að breyta þessu í stillingum WiFi-leitar. "<annotation id="link">"Breyta"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Slökkva á flugstillingu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velja notanda"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 435c549..4b10d9f 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nessun dispositivo disponibile"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nessuna connessione Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosità"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverti colori"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversione dei colori"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Altre impostazioni"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Impostazioni utente"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fine"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ingrandisci l\'intero schermo"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Opzione"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Il pulsante Accessibilità ha sostituito il gesto di accessibilità\n\n"<annotation id="link">"Visualizza le impostazioni"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tocca per aprire funzioni di accessibilità. Personalizza o sostituisci il pulsante in Impostazioni.\n\n"<annotation id="link">"Vedi impostazioni"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sposta il pulsante fino al bordo per nasconderlo temporaneamente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sposta in alto a sinistra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sposta in alto a destra"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per cambiare rete, scollega il cavo Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Per migliorare l\'esperienza con il dispositivo, le app e i servizi possono continuare a cercare reti Wi-Fi in qualsiasi momento, anche quando la connessione Wi-Fi non è attiva. Puoi modificare questa preferenza nelle impostazioni relative alla ricerca di reti Wi-Fi. "<annotation id="link">"Cambia"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Disattiva la modalità aereo"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleziona utente"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f3bbbff..41c4dcb 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"אין מכשירים זמינים"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"אין חיבור ל-Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"בהירות"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"היפוך צבעים"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"היפוך צבעים"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"הגדרות נוספות"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"הגדרות המשתמש"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"בוצע"</string>
@@ -753,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"הגדלה של המסך המלא"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"מעבר"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"לחצן הנגישות החליף את תנועת הנגישות\n\n"<annotation id="link">"להצגת ההגדרות"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"מקישים כדי לפתוח את תכונות הנגישות. אפשר להחליף את הלחצן או להתאים אותו אישית בהגדרות.\n\n"<annotation id="link">"הצגת ההגדרות"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"כדי להסתיר זמנית את הלחצן, יש להזיז אותו לקצה"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"העברה לפינה השמאלית העליונה"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"העברה לפינה הימנית העליונה"</string>
@@ -889,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות זאת בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"השבתה של מצב טיסה"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"בחירת משתמש"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 96dbbe7..452d4ae 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"利用可能なデバイスがありません"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi 未接続"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"画面の明るさ"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"色を反転"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色反転"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"詳細設定"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ユーザー設定"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完了"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"画面全体を拡大します"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"画面の一部を拡大します"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"スイッチ"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ユーザー補助ジェスチャーに代わって、ユーザー補助機能ボタンが導入されました\n\n"<annotation id="link">"設定を表示"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"タップしてユーザー補助機能を開きます。ボタンのカスタマイズや入れ替えを [設定] で行えます。\n\n"<annotation id="link">"設定を表示"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ボタンを一時的に非表示にするには、端に移動させてください"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"左上に移動"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"右上に移動"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ネットワークを変更するにはイーサネット接続を解除してください"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"デバイスの機能向上のため、アプリやサービスは、Wi-Fi が OFF の場合でも、いつでも Wi-Fi ネットワークをスキャンできます。この設定は Wi-Fi スキャンの設定で変更できます。"<annotation id="link">"変更"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"機内モードを OFF にする"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ユーザーの選択"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 7a43c21..3f301d1 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"მოწყობილობები მიუწვდომელია"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi არ არის დაკავშირებული"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"განათება"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ფერების შებრუნება"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ფერთა ინვერსია"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"დამატებითი პარამეტრები"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"მომხმარებლის პარამეტრები"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"დასრულდა"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"გაადიდეთ სრულ ეკრანზე"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"გადართვა"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"მარტივი წვდომის ღილაკმა ჩაანაცვლა მარტივი წვდომის ჟესტი\n\n"<annotation id="link">"პარამეტრების ნახვა"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"შეეხეთ მარტივი წვდომის ფუნქციების გასახსნელად. მოარგეთ ან შეცვალეთ ეს ღილაკი პარამეტრებში.\n\n"<annotation id="link">"პარამეტრების ნახვა"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"გადაიტანეთ ღილაკი კიდეში, რათა დროებით დამალოთ ის"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ზევით და მარცხნივ გადატანა"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ზევით და მარჯვნივ გადატანა"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ქსელების გადასართავად, გაწყვიტეთ Ethernet-თან კავშირი"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"მოწყობილობისგან მიღებული გამოცდილების გასაუმჯობესებლად, აპებსა და სერვისებს მაინც შეუძლია სკანირება Wi‑Fi ქსელების აღმოსაჩენად, ნებისმიერ დროს, მაშინაც კი, როცა Wi‑Fi გამორთულია. ამის შეცვლა Wi-Fi სკანირების პარამეტრებში შეგიძლიათ. "<annotation id="link">"შეცვლა"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"თვითმფრინავის რეჟიმის გამორთვა"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"მომხმარებლის არჩევა"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 8ae4699..1cab7fe 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Құрылғылар қол жетімді емес"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi желісіне жалғанбаған"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарықтығы"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Түс инверсиясы"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түс инверсиясы"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Қосымша параметрлер"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Пайдаланушы параметрлері"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Дайын"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Толық экранды ұлғайту"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ауысу"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Арнайы мүмкіндіктер қимылының орнына \"Арнайы мүмкіндіктер\" түймесі болады.\n\n"<annotation id="link">"Параметрлерді көру"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Арнайы мүмкіндікті ашу үшін түртіңіз. Түймені параметрден реттеңіз не ауыстырыңыз.\n\n"<annotation id="link">"Параметрді көру"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Түймені уақытша жасыру үшін оны шетке қарай жылжытыңыз."</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жоғарғы сол жаққа жылжыту"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жоғарғы оң жаққа жылжыту"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Желілерді ауыстыру үшін ethernet кабелін ажыратыңыз."</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Құрылғы жұмысын жақсарту үшін қолданбалар мен қызметтер Wi-Fi байланысы өшірулі кезде де Wi-Fi желілерін іздейді. Оны Wi-Fi іздеу параметрлерінен өзгерте аласыз. "<annotation id="link">"Өзгерту"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Ұшақ режимін өшіру"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Пайдаланушыны таңдау"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index d4207c7..f858339 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -233,7 +233,7 @@
<string name="quick_settings_location_label" msgid="2621868789013389163">"ទីតាំង"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ការចូលប្រើកាមេរ៉ា"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"ការចូលប្រើមីក្រូហ្វូន"</string>
- <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"អាចប្រើបាន"</string>
+ <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"អាចចូលប្រើបាន"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"បានទប់ស្កាត់"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ឧបករណ៍មេឌៀ"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"អ្នកប្រើ"</string>
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"មិនមានឧបករណ៍ដែលអាចប្រើបាន"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"មិនមានការតភ្ជាប់ Wi-Fi ទេ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ពន្លឺ"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ដាក់បញ្ច្រាសពណ៌"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ការបញ្ច្រាសពណ៌"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ការកំណត់ច្រើនទៀត"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ការកំណត់អ្នកប្រើប្រាស់"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"រួចរាល់"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ពង្រីកពេញអេក្រង់"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីកផ្នែកនៃអេក្រង់"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ប៊ូតុងបិទបើក"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ប៊ូតុងភាពងាយស្រួលបានជំនួសចលនាភាពងាយស្រួល\n\n"<annotation id="link">"មើលការកំណត់"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ចុចដើម្បីបើកមុខងារភាពងាយស្រួល។ ប្ដូរ ឬប្ដូរប៊ូតុងនេះតាមបំណងនៅក្នុងការកំណត់។\n\n"<annotation id="link">"មើលការកំណត់"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ផ្លាស់ទីប៊ូតុងទៅគែម ដើម្បីលាក់វាជាបណ្ដោះអាសន្ន"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងឆ្វេង"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងស្ដាំ"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធន៍ប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"បិទមុខងារពេលជិះយន្តហោះ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ជ្រើសរើសអ្នកប្រើប្រាស់"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 0f096af..67518b9 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"ಯಾವುದೇ ಸಾಧನಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣ ಇನ್ವರ್ಟ್ ಮಾಡಿ"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ಕಲರ್ ಇನ್ವರ್ಶನ್"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ಬಳಕೆದಾರರ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಹಿಗ್ಗಿಸಿ"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ಸ್ಕ್ರೀನ್ನ ಅರ್ಧಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ಸ್ವಿಚ್"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್, ಪ್ರವೇಶಿಸುವಿಕೆ ಗೆಸ್ಚರ್ ಅನ್ನು ಬದಲಾಯಿಸಿದೆ\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಈ ಬಟನ್ ಅನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ ಅಥವಾ ಬದಲಾಯಿಸಿ.\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ಅದನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ಮರೆಮಾಡಲು ಅಂಚಿಗೆ ಬಟನ್ ಸರಿಸಿ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ಎಡ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಬದಲಿಸಲು, ಇಥರ್ನೆಟ್ ಅನ್ನು ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ವೈ-ಫೈ ಆಫ್ ಇದ್ದಾಗಲೂ ಸಹ, ಸಾಧನದ ಅನುಭವವನ್ನು ಸುಧಾರಿಸಲು, ಆ್ಯಪ್ಗಳು ಮತ್ತು ಸೇವೆಗಳು ಯಾವಾಗ ಬೇಕಾದರೂ ಸಹ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್ ಮಾಡಬಹುದು. ನೀವು ಇದನ್ನು ವೈ-ಫೈ ಸ್ಕ್ಯಾನಿಂಗ್ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಬದಲಾಯಿಸಬಹುದು. "<annotation id="link">"ಬದಲಿಸಿ"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ ಆಫ್ ಮಾಡಿ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ಬಳಕೆದಾರ ಆಯ್ಕೆಮಾಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index d6af474..bf6a095 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"사용 가능한 기기가 없습니다."</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi가 연결되지 않음"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"밝기"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"색상 반전"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"색상 반전"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"설정 더보기"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"사용자 설정"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"완료"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"전체 화면 확대"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"전환"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"접근성 동작이 접근성 버튼으로 대체되었습니다.\n\n"<annotation id="link">"설정 보기"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"접근성 기능을 열려면 탭하세요. 설정에서 이 버튼을 맞춤설정하거나 교체할 수 있습니다.\n\n"<annotation id="link">"설정 보기"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"버튼을 가장자리로 옮겨서 일시적으로 숨기세요."</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"왼쪽 상단으로 이동"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"오른쪽 상단으로 이동"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"네트워크를 전환하려면 이더넷을 연결 해제하세요."</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"기기 환경을 개선하기 위해 Wi‑Fi가 꺼져 있을 때도 앱과 서비스에서 Wi‑Fi 네트워크를 검색할 수 있습니다. 이 설정은 Wi‑Fi 검색 설정에서 변경할 수 있습니다. "<annotation id="link">"변경"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"비행기 모드 사용 중지"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"사용자 선택"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 686546c..3fecd58 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Жеткиликтүү түзмөктөр жок"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi туташкан жок"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарыктыгы"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Түстөрдү инверсиялоо"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түстү инверсиялоо"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Дагы жөндөөлөр"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Колдонуучунун жөндөөлөрү"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Бүттү"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Толук экранда ачуу"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Которулуу"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Атайын мүмкүнчүлүктөр жаңсоосунун ордуна атайын мүмкүнчүлүктөр баскычы колдонулмакчы\n\n"<annotation id="link">"Жөндөөлөрдү көрүү"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Атайын мүмкүнчүлүктөрдү ачуу үчүн басыңыз. Бул баскычты Жөндөөлөрдөн өзгөртүңүз.\n\n"<annotation id="link">"Жөндөөлөрдү көрүү"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Баскычты убактылуу жашыра туруу үчүн экрандын четине жылдырыңыз"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жогорку сол жакка жылдыруу"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жогорку оң жакка жылдырыңыз"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Башка тармактарга которулуу үчүн Ethernet кабелин ажыратыңыз"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Түзмөктүн колдонулушун жакшыртуу үчүн колдонмолор менен кызматтар Wi‑Fi өчүп турса да зымсыз тармактарды издей беришет. Аны Wi-Fi тармактарын издөө жөндөөлөрүнөн өзгөртө аласыз. "<annotation id="link">"Өзгөртүү"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Учак режимин өчүрүү"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Колдонуучуну тандоо"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index c456f30..e0a8476 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"ບໍ່ມີອຸປະກອນທີ່ສາມາດໃຊ້ໄດ້"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ບໍ່ໄດ້ເຊື່ອມຕໍ່ Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ຄວາມແຈ້ງ"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ສະຫຼັບສີ"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ການປີ້ນສີ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ການຕັ້ງຄ່າເພີ່ມເຕີມ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ຕັ້ງຄ່າຜູ້ໃຊ້"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ແລ້ວໆ"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ຂະຫຍາຍເຕັມຈໍ"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ສະຫຼັບ"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ປຸ່ມການຊ່ວຍເຂົ້າເຖິງຖືກແທນທີ່ທ່າທາງຊ່ວຍເຂົ້າເຖິງແລ້ວ\n\n"<annotation id="link">"ເບິ່ງການຕັ້ງຄ່າ"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ແຕະເພື່ອເປີດຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ປັບແຕ່ງ ຫຼື ປ່ຽນປຸ່ມນີ້ໃນການຕັ້ງຄ່າ.\n\n"<annotation id="link">"ເບິ່ງການຕັ້ງຄ່າ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ຍ້າຍປຸ່ມໄປໃສ່ຂອບເພື່ອເຊື່ອງມັນຊົ່ວຄາວ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ຍ້າຍຊ້າຍເທິງ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ຍ້າຍຂວາເທິງ"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ເພື່ອສະຫຼັບເຄືອຂ່າຍ, ໃຫ້ຕັດການເຊື່ອມຕໍ່ອີເທີເນັດກ່ອນ"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ເພື່ອປັບປຸງປະສົບການອຸປະກອນ, ແອັບ ແລະ ບໍລິການຍັງຄົງສາມາດສະແກນຫາເຄືອຂ່າຍ Wi‑Fi ຕອນໃດກໍໄດ້, ເຖິງແມ່ນວ່າຈະປິດ Wi‑Fi ໄວ້ກໍຕາມ. ທ່ານສາມາດປ່ຽນສິ່ງນີ້ໄດ້ໃນການຕັ້ງຄ່າການສະແກນ Wi‑Fi. "<annotation id="link">"ປ່ຽນ"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ປິດໂໝດຢູ່ໃນຍົນ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ເລືອກຜູ້ໃຊ້"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 7c5dc4d..dbf85c2 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nėra pasiekiamų įrenginių"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"„Wi-Fi“ neprijungtas"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Šviesumas"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Pakeisti spalvas"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Spalvų inversija"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Daugiau nustatymų"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Naudotojo nustatymai"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Atlikta"</string>
@@ -753,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Viso ekrano didinimas"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Perjungti"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pritaikomumo gestas pakeistas pritaikomumo mygtuku\n\n"<annotation id="link">"Žr. nustatymus"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Palietę atidarykite pritaikymo neįgaliesiems funkcijas. Tinkinkite arba pakeiskite šį mygtuką nustatymuose.\n\n"<annotation id="link">"Žr. nustatymus"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Perkelkite mygtuką prie krašto, kad laikinai jį paslėptumėte"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Perkelti į viršų kairėje"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Perkelti į viršų dešinėje"</string>
@@ -889,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Norėdami perjungti tinklus, atjunkite eternetą"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Kad pagerintų įrenginio funkcijas, programos ir paslaugos vis tiek gali bet kada nuskaityti ieškodamos „Wi‑Fi“ tinklų, net jei „Wi‑Fi“ išjungtas. Tai galite pakeisti „Wi-Fi“ nuskaitymo nustatymuose. "<annotation id="link">"Pakeisti"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Išjungti lėktuvo režimą"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Naudotojo pasirinkimas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index ec5d108..c87ba44 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -250,7 +250,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nav pieejamu ierīču."</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nav izveidots savienojums ar Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Spilgtums"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invertēt krāsas"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Krāsu inversija"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Vairāk iestatījumu"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Lietotāja iestatījumi"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gatavs"</string>
@@ -748,7 +748,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Palielināt visu ekrānu"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pārslēgt"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pieejamības žests ir aizstāts ar pieejamības pogu\n\n"<annotation id="link">"Skatīt iestatījumus"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atveriet pieejamības funkcijas. Pielāgojiet vai aizstājiet šo pogu iestatījumos.\n\n"<annotation id="link">"Skatīt iestatījumus"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Lai īslaicīgi paslēptu pogu, pārvietojiet to uz malu"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pārvietot augšpusē pa kreisi"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pārvietot augšpusē pa labi"</string>
@@ -883,5 +883,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Lai pārslēgtu tīklus, atvienojiet tīkla Ethernet vadu."</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Lai uzlabotu ierīces lietošanas iespējas, lietotnes un pakalpojumi joprojām varēs meklēt Wi‑Fi tīklus jebkurā laikā, pat ja Wi‑Fi būs izslēgts. Varat to mainīt Wi‑Fi meklēšanas iestatījumos. "<annotation id="link">"Mainīt"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Izslēgt lidojuma režīmu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Lietotāja atlase"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index c09dfaa..9d6fdc8 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Нема достапни уреди"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi не е поврзано"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветленост"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Преврти ги боите"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија на боите"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Повеќе поставки"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Поставки на корисникот"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Зголемете го целиот екран"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголемувајте дел од екранот"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Префрли"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Копчето за пристапност го замени движењето за пристапност\n\n"<annotation id="link">"Прикажи поставки"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Допрете за функциите за пристапност. Приспособете или заменете го копчево во „Поставки“.\n\n"<annotation id="link">"Прикажи поставки"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете го копчето до работ за да го сокриете привремено"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За промена на мрежата, прекинете ја врската со етернетот"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"За да се подобри доживувањето на уредот, апликациите и услугите може сѐ уште да скенираат за Wi‑Fi мрежи во секое време, дури и кога Wi‑Fi е исклучено. Може да го промените ова во поставките за „Скенирање за Wi-Fi“. "<annotation id="link">"Промени"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Исклучи го авионскиот режим"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изберете корисник"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 176ddc9..fd71a06 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"ഉപകരണങ്ങളൊന്നും ലഭ്യമല്ല"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"വൈഫൈ കണക്റ്റ് ചെയ്തിട്ടില്ല"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നെഗറ്റീവ് ലുക്ക്"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"നിറം വിപരീതമാക്കൽ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"കൂടുതൽ ക്രമീകരണങ്ങൾ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ഉപയോക്തൃ ക്രമീകരണം"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"സ്ക്രീൻ പൂർണ്ണമായും മാഗ്നിഫൈ ചെയ്യുക"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"സ്ക്രീനിന്റെ ഭാഗം മാഗ്നിഫൈ ചെയ്യുക"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"മാറുക"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ഉപയോഗസഹായി ജെസ്ച്ചറിനെ മാറ്റി പകരം ഉപയോഗസഹായി ബട്ടൺ വന്നു\n\n"<annotation id="link">"ക്രമീകരണം കാണുക"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ഉപയോഗസഹായി ഫീച്ചർ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ക്രമീകരണത്തിൽ ഈ ബട്ടൺ ഇഷ്ടാനുസൃതമാക്കാം, മാറ്റാം.\n\n"<annotation id="link">"ക്രമീകരണം കാണൂ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"തൽക്കാലം മറയ്ക്കുന്നതിന് ബട്ടൺ അരുകിലേക്ക് നീക്കുക"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"മുകളിൽ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"മറ്റ് നെറ്റ്വർക്കുകളിലേക്ക് മാറാൻ, ഇതർനെറ്റ് വിച്ഛേദിക്കുക"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ഉപകരണ അനുഭവം മെച്ചപ്പെടുത്താൻ, വൈഫൈ ഓഫാക്കിയിരിക്കുമ്പോൾ പോലും ആപ്പുകൾക്കും സേവനങ്ങൾക്കും വൈഫൈ നെറ്റ്വർക്കുകൾ കണ്ടെത്താൻ ഏത് സമയത്തും സ്കാൻ ചെയ്യാനാകും. നിങ്ങൾക്ക് ഇത് വൈഫൈ സ്കാനിംഗ് ക്രമീകരണത്തിൽ മാറ്റാം. "<annotation id="link">"മാറ്റുക"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ഫ്ലൈറ്റ് മോഡ് ഓഫാക്കുക"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ഉപയോക്താവിനെ തിരഞ്ഞെടുക്കൂ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 09776bd..a499922 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Төхөөрөмж байхгүй"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi-д холбогдоогүй байна"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Тодрол"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Өнгийг урвуулах"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Өнгө урвуулах"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Бусад тохиргоо"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Хэрэглэгчийн тохиргоо"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Дууссан"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Бүтэн дэлгэцийг томруулах"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Сэлгэх"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Хандалтын товчлуурыг хандалтын зангаагаар сольсон\n\n"<annotation id="link">"Тохиргоо харах"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Хандалтын онцлогуудыг нээхийн тулд товшино уу. Энэ товчлуурыг Тохиргоо хэсэгт өөрчилж эсвэл солиорой.\n\n"<annotation id="link">"Тохиргоог харах"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Үүнийг түр нуухын тулд товчлуурыг зах руу зөөнө үү"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Зүүн дээш зөөх"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Баруун дээш зөөх"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Сүлжээг сэлгэхийн тулд этернэтийг салгана уу"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Төхөөрөмжийн туршлагыг сайжруулахын тулд аппууд болон үйлчилгээнүүд нь Wi-Fi сүлжээг хүссэн үедээ буюу Wi-Fi-г унтраалттай байсан ч скан хийх боломжтой хэвээр байна. Та үүнийг Wi-Fi скан хийх тохиргоонд өөрчлөх боломжтой. "<annotation id="link">"Өөрчлөх"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Нислэгийн горимыг унтраах"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Хэрэглэгч сонгох"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 494956f..9996d41 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"कोणतेही डिव्हाइसेस उपलब्ध नाहीत"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"वाय-फाय नाही"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"चमक"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"रंग उलटे करा"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्व्हर्जन"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"अधिक सेटिंग्ज"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"वापरकर्ता सेटिंग्ज"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"पूर्ण झाले"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फुल स्क्रीन मॅग्निफाय करा"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अॅक्सेसिबिलिटी जेश्चर हे आता अॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पहा"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"अॅक्सेसिबिलिटी वैशिष्ट्ये उघडण्यासाठी, टॅप करा. सेटिंग्जमध्ये हे बटण कस्टमाइझ करा किंवा बदला.\n\n"<annotation id="link">"सेटिंग्ज पहा"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्यामध्ये हलवा"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"वर डावीकडे हलवा"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"वर उजवीकडे हलवा"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क स्विच करण्यासाठी, इथरनेट केबल डिस्कनेक्ट करा"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"डिव्हाइसच्या अनुभवामध्ये सुधारणा करण्यासाठी, वाय-फाय बंद असले तरीही ॲप्स आणि सेवा या कधीही वाय-फाय नेटवर्क स्कॅन करू शकतात. तुम्ही हे वाय-फाय स्कॅनिंग सेटिंग्जमध्ये बदलू शकता. "<annotation id="link">"बदला"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"विमान मोड बंद करा"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"वापरकर्ता निवडा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 90977cf..5764807 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Pengambilan tangkapan skrin tidak dibenarkan oleh apl atau organisasi anda"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Edit"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Edit tangkapan skrin"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Kongsi tangkapan skrin"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Tangkap lebih banyak"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ketepikan tangkapan skrin"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string>
@@ -250,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Tiada peranti tersedia"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi tidak disambungkan"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Songsangkan warna"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Penyongsangan warna"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Lagi tetapan"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Tetapan pengguna"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
@@ -744,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Besarkan skrin penuh"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Besarkan sebahagian skrin"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Tukar"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butang kebolehaksesan menggantikan gerak isyarat kebolehaksesan\n\n"<annotation id="link">"Lihat tetapan"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ketik untuk membuka ciri kebolehaksesan. Sesuaikan/gantikan butang ini dalam Tetapan.\n\n"<annotation id="link">"Lihat tetapan"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Gerakkan butang ke tepi untuk disembunyikan buat sementara waktu"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Alihkan ke atas sebelah kiri"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Alihkan ke atas sebelah kanan"</string>
@@ -878,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk menukar rangkaian, putuskan sambungan ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Untuk meningkatkan pengalaman peranti, apl dan perkhidmatan masih dapat melakukan imbasan untuk mengesan rangkaian Wi-Fi pada bila-bila masa, meskipun apabila Wi-Fi dimatikan. Anda boleh menukar tetapan ini dalam tetapan pengimbasan Wi-Fi. "<annotation id="link">"Tukar"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Matikan mod pesawat"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index a9db254..a1e0668 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"တည်းဖြတ်ရန်"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"ဖန်သားပြင်ဓာတ်ပုံကို တည်းဖြတ်သည်"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"ဖန်သားပြင်ဓာတ်ပုံကို မျှဝေနိုင်သည်"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"နောက်ထပ် ရိုက်ကူးရန်"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ဖန်သားပြင်ဓာတ်ပုံကို ပယ်သည်"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string>
@@ -250,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"ကိရိယာများ မရှိ"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi ချိတ်ဆက်ထားခြင်းမရှိပါ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"အလင်းတောက်ပမှု"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"အရောင်ပြောင်းပြန်"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"အရောင်ပြောင်းပြန်ပြုလုပ်ရန်"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"နောက်ထပ် ဆက်တင်များ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"အသုံးပြုသူ ဆက်တင်များ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ပြီးပါပြီ"</string>
@@ -526,7 +525,7 @@
<string name="notification_menu_gear_description" msgid="6429668976593634862">"အကြောင်းကြားချက် ထိန်းချုပ်မှုများ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"အကြောင်းကြားချက်များကို ဆိုင်းငံ့ရန် ရွေးချယ်စရာများ"</string>
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"ကျွန်ုပ်ကို သတိပေးပါ"</string>
- <string name="snooze_undo" msgid="2738844148845992103">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
+ <string name="snooze_undo" msgid="2738844148845992103">"နောက်ပြန်ရန်"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> ဆိုင်းငံ့ရန်"</string>
<plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
<item quantity="other">%d နာရီ</item>
@@ -744,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ဖန်သားပြင်အပြည့် ချဲ့သည်"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်းကို ချဲ့ပါ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ခလုတ်"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"အများသုံးစွဲနိုင်မှုခလုတ်က အများသုံးစွဲနိုင်မှုလက်ဟန်ကို အစားထိုးသည်\n\n"<annotation id="link">"ဆက်တင်များကို ကြည့်ပါ"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်ရန် တို့ပါ။ ဆက်တင်များတွင် ဤခလုတ်ကို စိတ်ကြိုက်ပြင်ပါ (သို့) လဲပါ။\n\n"<annotation id="link">"ဆက်တင်များ ကြည့်ရန်"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ခလုတ်ကို ယာယီဝှက်ရန် အစွန်းသို့ရွှေ့ပါ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ဘယ်ဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ညာဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
@@ -878,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ကွန်ရက်ပြောင်းရန် အီသာနက်ကို ချိတ်ဆက်မှုဖြုတ်ပါ"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"စက်ပစ္စည်းကို ပိုမိုကောင်းမွန်စွာ အသုံးပြုနိုင်ရန် Wi-Fi ပိတ်ထားသည့်တိုင် အက်ပ်နှင့် ဝန်ဆောင်မှုများက Wi-Fi ကွန်ရက်များကို အချိန်မရွေး စကင်ဖတ်နိုင်သည်။ ၎င်းကို Wi-Fi ရှာဖွေခြင်း ဆက်တင်များတွင် ပြောင်းနိုင်သည်။ "<annotation id="link">"ပြောင်းရန်"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"လေယာဉ်ပျံမုဒ်ကို ပိတ်ရန်"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"အသုံးပြုသူ ရွေးခြင်း"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index c1fa028..c1e334a 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Ingen enheter er tilgjengelige"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi er ikke tilkoblet"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverter farger"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Fargeinvertering"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Flere innstillinger"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Brukerinnstillinger"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Ferdig"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Forstørr hele skjermen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Bytt"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tilgjengelighet-knappen har erstattet tilgjengelighetsbevegelsen\n\n"<annotation id="link">"Se innstillingene"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Trykk for å åpne tilgj.funksjoner. Tilpass eller bytt knappen i Innstillinger.\n\n"<annotation id="link">"Se innstillingene"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytt knappen til kanten for å skjule den midlertidig"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytt til øverst til venstre"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytt til øverst til høyre"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"For å bytte nettverk, koble fra Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"For å forbedre brukeropplevelsen på enheten kan apper og tjenester søke etter Wi-Fi-nettverk når som helst – også når Wi-Fi er slått av. Du kan endre dette i innstillingene for Wi-Fi-skanning. "<annotation id="link">"Bytt"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Slå av flymodus"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velg bruker"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 9a609cf..e70a30d 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"कुनै उपकरणहरू उपलब्ध छैन"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi जडान गरिएको छैन"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"उज्यालपन"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"रंग उल्टाउनुहोस्"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्भर्सन"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"थप सेटिङहरू"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"प्रयोगकर्तासम्बन्धी सेटिङ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"भयो"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"पूरै स्क्रिन जुम इन गर्नुहोस्"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"बदल्नुहोस्"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"एक्सेसिबिलिटी इसाराका स्थानमा एक्सेसिबिलिटी बटन प्रयोग हुन थालेको छ\n\n"<annotation id="link">"सेटिङ हेर्नुहोस्"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"सर्वसुलभता कायम गर्ने सुविधा खोल्न ट्याप गर्नुहोस्। सेटिङमा गई यो बटन कस्टमाइज गर्नुहोस् वा बदल्नुहोस्।\n\n"<annotation id="link">"सेटिङ हेर्नुहोस्"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"यो बटन केही बेर नदेखिने पार्न किनारातिर सार्नुहोस्"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सिरानको बायाँतिर सार्नुहोस्"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सिरानको दायाँतिर सार्नुहोस्"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदल्न इथरनेट डिस्कनेक्ट गर्नुहोस्"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"डिभाइस प्रयोगको अनुभवमा गुणस्तर सुधार गर्न, एप तथा सेवाहरूले अझै पनि जुनसुकै बेला (Wi‑Fi अफ भएका बेलामा पनि) Wi‑Fi नेटवर्क खोज्न सक्छन्। तपाईं यसलाई Wi‑Fi स्क्यानिङका सेटिङमा गई परिवर्तन गर्न सक्नुहुन्छ। "<annotation id="link">"बदल्नुहोस्"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"हवाइजहाज मोड अफ गर्नुहोस्"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"प्रयोगकर्ता चयन गर्नु…"</string>
</resources>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index b98694e..fcb3698 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -77,7 +77,7 @@
<color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
<!-- UDFPS colors -->
- <color name="udfps_enroll_icon">#ffffff</color> <!-- 100% white -->
+ <color name="udfps_enroll_icon">#7DA7F1</color>
<color name="GM2_green_500">#FF41Af6A</color>
<color name="GM2_blue_500">#5195EA</color>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 07e28b6..cb963e6 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -16,9 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog">
- <item name="android:buttonCornerRadius">28dp</item>
- </style>
+ <style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Dialog"/>
<style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" />
@@ -53,13 +51,17 @@
<style name="TextAppearance.InternetDialog.Active">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/connected_network_primary_color</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textDirection">locale</item>
</style>
<style name="TextAppearance.InternetDialog.Secondary.Active">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/connected_network_secondary_color</item>
+ <item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
+ </style>
+
+ <style name="InternetDialog.Divider.Active">
+ <item name="android:background">?android:attr/textColorSecondaryInverse</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e749ff8..cff0471 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Geen apparaten beschikbaar"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wifi niet verbonden"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Kleuren omkeren"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleurinversie"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellingen"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Gebruikersinstellingen"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Volledig scherm vergroten"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Deel van het scherm vergroten"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schakelen"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"De knop Toegankelijkheid vervangt het toegankelijkheidsgebaar\n\n"<annotation id="link">"Instellingen bekijken"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tik voor toegankelijkheidsfuncties. Wijzig of vervang deze knop via Instellingen.\n\n"<annotation id="link">"Naar Instellingen"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Knop naar de rand verplaatsen om deze tijdelijk te verbergen"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Naar linksboven verplaatsen"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Naar rechtsboven verplaatsen"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Verbreek de ethernetverbinding om van netwerk te wisselen"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Apps en services kunnen nog steeds op elk moment scannen op wifi-netwerken, zelfs als wifi uitstaat, om de apparaatfunctionaliteit te verbeteren. Je kunt dit aanpassen in de instellingen voor wifi-scannen. "<annotation id="link">"Wijzigen"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Vliegtuigmodus uitzetten"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Gebruiker selecteren"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index fc24fe4..78ea060 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"କୌଣସି ଡିଭାଇସ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ୱାଇ-ଫାଇ ସଂଯୋଜିତ ହୋଇନାହିଁ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ଉଜ୍ଜ୍ୱଳତା"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ରଙ୍ଗ ଇନଭାର୍ଟ୍ କରନ୍ତୁ"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ରଙ୍ଗ ଇନଭାର୍ସନ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ଅଧିକ ସେଟିଂସ୍"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ଉପଯୋଗକର୍ତ୍ତା ସେଟିଂସ୍"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ହୋଇଗଲା"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ମ୍ୟାଗ୍ନିଫାଏ କରନ୍ତୁ"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ଆକ୍ସେସିବିଲିଟୀ ଜେଶ୍ଚରକୁ ଆକ୍ସେସିବିଲିଟୀ ବଟନରେ ପରିବର୍ତ୍ତନ କରାଯାଇଛି\n\n"<annotation id="link">"ସେଟିଂସ୍ ଦେଖନ୍ତୁ"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ଆକ୍ସେସିବିଲିଟୀ ଫିଚର ଖୋଲିବାକୁ ଟାପ କରନ୍ତୁ। ସେଟିଂସରେ ଏହି ବଟନକୁ କଷ୍ଟମାଇଜ କର କିମ୍ବା ବଦଳାଅ।\n\n"<annotation id="link">"ସେଟିଂସ ଦେଖନ୍ତୁ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ବଟନକୁ ଅସ୍ଥାୟୀ ଭାବେ ଲୁଚାଇବା ପାଇଁ ଏହାକୁ ଗୋଟିଏ ଧାରକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ଶୀର୍ଷ ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ଶୀର୍ଷ ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ନେଟୱାର୍କ ସ୍ୱିଚ୍ କରିବାକୁ, ଇଥରନେଟ୍ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ଡିଭାଇସ ଅନୁଭୂତିକୁ ଉନ୍ନତ କରିବା ପାଇଁ, ୱାଇ-ଫାଇ ବନ୍ଦ ଥିଲେ ମଧ୍ୟ ଆପ ଓ ସେବାଗୁଡ଼ିକ ଏବେ ବି ଯେ କୌଣସି ସମୟରେ ୱାଇ-ଫାଇ ନେଟୱାର୍କ ପାଇଁ ସ୍କାନ କରିପାରିବ। ଆପଣ ଏହାକୁ ୱାଇ-ଫାଇ ସ୍କାନିଂ ସେଟିଂସରେ ପରିବର୍ତ୍ତନ କରିପାରିବେ। "<annotation id="link">"ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ଏୟାରପ୍ଲେନ ମୋଡ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ଉପଯୋଗକର୍ତ୍ତା ଚୟନ କର"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index b6f6587..81b3fde 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"ਕੋਈ ਡਿਵਾਈਸਾਂ ਉਪਲਬਧ ਨਹੀਂ"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ਵਾਈ-ਫਾਈ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ਚਮਕ"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ਰੰਗ ਪਲਟਾਓ"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ਰੰਗ ਪਲਟਨਾ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ਵਰਤੋਂਕਾਰ ਸੈਟਿੰਗਾਂ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ਹੋ ਗਿਆ"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਵੱਡਦਰਸ਼ੀ ਕਰੋ"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ਸਵਿੱਚ"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਪਹੁੰਚਯੋਗਤਾ ਸੰਕੇਤ ਨਾਲ ਬਦਲ ਦਿੱਤਾ ਗਿਆ\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ ਦੇਖੋ"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਹ ਬਟਨ ਵਿਉਂਤਬੱਧ ਕਰੋ ਜਾਂ ਬਦਲੋ।\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ ਦੇਖੋ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ਬਟਨ ਨੂੰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਲੁਕਾਉਣ ਲਈ ਕਿਨਾਰੇ \'ਤੇ ਲਿਜਾਓ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ਉੱਪਰ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਈਥਰਨੈੱਟ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"ਡੀਵਾਈਸ ਦੇ ਅਨੁਭਵ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ, ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਕਰ ਸਕਦੀਆਂ ਹਨ, ਭਾਵੇਂ ਵਾਈ-ਫਾਈ ਬੰਦ ਹੀ ਕਿਉਂ ਨਾ ਹੋਵੇ। ਤੁਸੀਂ ਇਸ ਨੂੰ ਵਾਈ‑ਫਾਈ ਸਕੈਨਿੰਗ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਬਦਲ ਸਕਦੇ ਹੋ। "<annotation id="link">"ਬਦਲੋ"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਬੰਦ ਕਰੋ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ਵਰਤੋਂਕਾਰ ਚੁਣੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 564e037..0b19747 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Brak dostępnych urządzeń"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Brak połączenia z Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jasność"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Odwróć kolory"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Odwrócenie kolorów"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Więcej ustawień"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ustawienia użytkownika"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotowe"</string>
@@ -753,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Powiększanie pełnego ekranu"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Przełącz"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Przycisk ułatwień dostępu zastąpił gest ułatwień dostępu\n\n"<annotation id="link">"Wyświetl ustawienia"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Kliknij, aby otworzyć ułatwienia dostępu. Dostosuj lub zmień ten przycisk w Ustawieniach.\n\n"<annotation id="link">"Wyświetl ustawienia"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Przesuń przycisk do krawędzi, aby ukryć go tymczasowo"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Przenieś w lewy górny róg"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Przenieś w prawy górny róg"</string>
@@ -889,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aby przełączać sieci, odłącz Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Aby zapewnić Ci większy komfort korzystania z urządzenia, aplikacje i usługi mogą nadal wyszukiwać sieci Wi-Fi w pobliżu nawet wtedy, gdy Wi-Fi jest wyłączone. Możesz to zmienić w ustawieniach skanowania Wi-Fi. "<annotation id="link">"Zmień"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Wyłącz tryb samolotowy"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Wybierz użytkownika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 324190f0..32cb8d5 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Não há dispositivos disponíveis"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi não conectado"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverter cores"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Mais configurações"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Config. do usuário"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar toda a tela"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão de acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Veja as configurações"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir os recursos de acessibilidade. Personalize ou substitua o botão nas Configurações.\n\n"<annotation id="link">"Ver configurações"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para melhorar a experiência no dispositivo, os apps e serviços ainda podem procurar redes Wi-Fi a qualquer momento, mesmo quando o Wi-Fi estiver desativado. Você pode mudar essa opção nas configurações de busca por Wi-Fi. "<annotation id="link">"Mudar"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Desativar modo avião"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 8e0008d..1ec2492 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Sem dispositivos disponíveis"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi não ligado"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverter cores"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Mais definições"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Definições do utilizador"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar o ecrã inteiro"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte do ecrã"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Mudar"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão Acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Ver definições"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir funcionalidades de acessibilidade. Personal. ou substitua botão em Defin.\n\n"<annotation id="link">"Ver defin."</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a extremidade para o ocultar temporariamente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover p/ parte sup. esquerda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover parte superior direita"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desligue a Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para melhorar a experiência do dispositivo, as apps e os serviços podem continuar a procurar redes Wi-Fi em qualquer altura, mesmo quando o Wi-Fi está desativado. Pode alterar esta opção nas definições de procura de Wi-Fi. "<annotation id="link">"Alterar"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Desative o modo de avião"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecione utilizador"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 324190f0..32cb8d5 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Não há dispositivos disponíveis"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi não conectado"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverter cores"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Mais configurações"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Config. do usuário"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar toda a tela"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão de acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Veja as configurações"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir os recursos de acessibilidade. Personalize ou substitua o botão nas Configurações.\n\n"<annotation id="link">"Ver configurações"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para melhorar a experiência no dispositivo, os apps e serviços ainda podem procurar redes Wi-Fi a qualquer momento, mesmo quando o Wi-Fi estiver desativado. Você pode mudar essa opção nas configurações de busca por Wi-Fi. "<annotation id="link">"Mudar"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Desativar modo avião"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 26c6c56..2b5a511 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -250,7 +250,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Niciun dispozitiv disponibil"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Rețeaua Wi-Fi nu este conectată"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminozitate"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inversați culorile"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversarea culorilor"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Mai multe setări"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setări de utilizator"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Terminat"</string>
@@ -748,7 +748,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Măriți tot ecranul"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Măriți o parte a ecranului"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Comutator"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butonul de accesibilitate a înlocuit gestul de accesibilitate\n\n"<annotation id="link">"Vedeți setările"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atingeți pentru a deschide funcțiile de accesibilitate. Personalizați sau înlocuiți butonul în Setări.\n\n"<annotation id="link">"Afișați setările"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mutați butonul spre margine pentru a-l ascunde temporar"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mutați în stânga sus"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mutați în dreapta sus"</string>
@@ -883,5 +883,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pentru a îmbunătăți experiența cu dispozitivul, aplicațiile și serviciile pot să caute în continuare rețele Wi‑Fi chiar și atunci când conexiunea Wi-Fi este dezactivată. Puteți să schimbați acest aspect din setările pentru căutarea de rețele Wi-Fi. "<annotation id="link">"Schimbați"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Dezactivați modul Avion"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alegeți utilizatorul"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 54fa293..0f3c4b4 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Изменить"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Изменить скриншот"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Поделиться скриншотом"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Увеличить площадь скриншота"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Закрыть скриншот"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string>
@@ -125,7 +124,7 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Подтверждено"</string>
<string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Нажмите \"Подтвердить\""</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"Аутентификация выполнена"</string>
- <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Использовать PIN-код"</string>
+ <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"PIN-код"</string>
<string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Использовать графический ключ"</string>
<string name="biometric_dialog_use_password" msgid="3445033859393474779">"Использовать пароль"</string>
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"Неверный PIN-код."</string>
@@ -252,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Нет доступных устройств"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Нет подключения к сети Wi-Fi."</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркость"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Обратные цвета"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверсия цветов"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Настройки"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Пользовательские настройки"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -754,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увеличение всего экрана"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Переключить"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Жест заменен на кнопку специальных возможностей\n\n"<annotation id="link">"Открыть настройки"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Нажмите, чтобы открыть спец. возможности. Настройте или замените эту кнопку в настройках.\n\n"<annotation id="link">"Настройки"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Чтобы временно скрыть кнопку, переместите ее к краю экрана"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перенести в левый верхний угол"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перенести в правый верхний угол"</string>
@@ -890,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Чтобы переключиться между сетями, отключите кабель Ethernet."</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Чтобы улучшать работу устройства, приложения и сервисы могут искать беспроводные сети в любое время, даже если вы отключили Wi‑Fi. Чтобы запретить это, отключите поиск сетей Wi‑Fi. "<annotation id="link">"Открыть настройки"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Отключить режим полета"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выберите профиль"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 1b4a0de..211a254 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"උපාංග නොතිබේ"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi සම්බන්ධ නොවීය"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"දීප්තිමත් බව"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"වර්ණ යටිකුරු කරන්න"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"වර්ණ අපවර්තනය"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"තව සැකසීම්"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"පරිශීලක සැකසීම්"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"නිමයි"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"පූර්ණ තිරය විශාලනය කරන්න"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ස්විචය"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ප්රවේශ්යතා බොත්තම ප්රවේශ්යතා ඉංගිතය ප්රතිස්ථාපනය කළේය\n\n"<annotation id="link">"සැකසීම් බලන්න"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ප්රවේශ්යතා විශේෂාංග විවෘත කිරීමට තට්ටු කරන්න. සැකසීම් තුළ මෙම බොත්තම අභිරුචිකරණය හෝ ප්රතිස්ථාපනය කරන්න.\n\n"<annotation id="link">"සැකසීම් බලන්න"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"එය තාවකාලිකව සැඟවීමට බොත්තම දාරයට ගෙන යන්න"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ඉහළ වමට ගෙන යන්න"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ඉහළ දකුණට ගෙන යන්න"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ජාල මාරු කිරීමට, ඊතර්නෙට් විසන්ධි කරන්න"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"උපාංග අත්දැකීම වැඩි දියුණු කිරිමට, Wi‑Fi ක්රියාවිරහිත විට පවා, ඕනෑම අවස්ථාවක Wi‑Fi ජාල සඳහා ස්කෑන් කිරීමට යෙදුම් සහ සේවාවලට හැකිය. ඔබට මෙය Wi‑Fi ස්කෑන් කිරීමේ සැකසීම් තුළ වෙනස් කළ හැකිය. "<annotation id="link">"වෙනස් කරන්න"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ගුවන් යානා ප්රකාරය ක්රියාවිරහිත කරන්න"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"පරිශීලක තෝරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 3633b53..47e6c03 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nie sú k dispozícii žiadne zariadenia"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Sieť Wi‑Fi nie je pripojená"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverzia farieb"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzia farieb"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Ďalšie nastavenia"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Používateľské nastavenia"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
@@ -753,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zväčšenie celej obrazovky"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prepnúť"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tlačidlo dostupnosti nahradilo gesto dostupnosti\n\n"<annotation id="link">"Zobraziť nastavenia"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Funkcie dostupnosti otvoríte klepnutím. Tlačidlo prispôsobte alebo nahraďte v Nastav.\n\n"<annotation id="link">"Zobraz. nast."</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ak chcete tlačidlo dočasne skryť, presuňte ho k okraju"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Presunúť doľava nahor"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Presunúť doprava nahor"</string>
@@ -889,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ak chcete prepnúť siete, odpojte ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Aplikácie a služby môžu kedykoľvek vyhľadávať siete Wi‑Fi (a to aj vtedy, keď je pripojenie Wi‑Fi vypnuté), čím zlepšujú prostredie v zariadení. Môžete to zmeniť v nastaveniach vyhľadávania sietí Wi‑Fi. "<annotation id="link">"Zmeniť"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Vypnúť režim v lietadle"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vyberte používateľa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 24c1e46..fac18c5 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Na voljo ni nobene naprave"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Povezava Wi-Fi ni vzpostavljena"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svetlost"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverzija barv"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija barv"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Več nastavitev"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Uporabniške nastavitve"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Končano"</string>
@@ -753,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Povečanje celotnega zaslona"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povečava dela zaslona"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Stikalo"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Gumb za funkcije za ljudi s posebnimi potrebami je zamenjal pripadajočo potezo.\n\n"<annotation id="link">"Ogled nastavitev"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dotaknite se za funkcije za ljudi s posebnimi potrebami. Ta gumb lahko prilagodite ali zamenjate v nastavitvah.\n\n"<annotation id="link">"Ogled nastavitev"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Če želite gumb začasno skriti, ga premaknite ob rob."</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premakni zgoraj levo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premakni zgoraj desno"</string>
@@ -889,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Če želite preklopiti omrežje, prekinite ethernetno povezavo."</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Za izboljšano izkušnjo pri uporabi naprave lahko aplikacije in storitve kadar koli iščejo omrežja Wi‑Fi, tudi ko je Wi‑Fi izklopljen. To lahko spremenite v nastavitvah iskanja omrežij Wi-Fi. "<annotation id="link">"Spremeni"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Izklopi način za letalo"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izberite uporabnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index f540f11..b566240 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Modifiko"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Modifiko pamjen e ekranit"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Ndaj pamjen e ekranit"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Regjistro më shumë"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Hiq pamjen e ekranit"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string>
@@ -250,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nuk ofrohet për përdorim asnjë pajisje"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi nuk është lidhur"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ndriçimi"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Shkëmbe ngjyrat"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Anasjellja e ngjyrës"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Cilësime të tjera"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Cilësimet e përdoruesit"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"U krye"</string>
@@ -744,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zmadho ekranin e plotë"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ndërro"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butoni i qasshmërisë zëvendësoi gjestin e qasshmërisë\n\n"<annotation id="link">"Shiko cilësimet"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Trokit dhe hap veçoritë e qasshmërisë. Modifiko ose ndërro butonin te \"Cilësimet\".\n\n"<annotation id="link">"Shih cilësimet"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Zhvendose butonin në skaj për ta fshehur përkohësisht"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Zhvendos lart majtas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Zhvendos lart djathtas"</string>
@@ -878,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Për të ndërruar rrjetet, shkëput Ethernet-in"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Për të përmirësuar përvojën e pajisjes, aplikacionet dhe shërbimet mund të vazhdojnë të skanojnë për rrjete Wi-Fi në çdo kohë, edhe kur Wi-Fi është joaktiv. Mund ta ndryshosh këtë te cilësimet e skanimit të Wi-Fi. "<annotation id="link">"Ndrysho"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Çaktivizo modalitetin e aeroplanit"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zgjidh përdoruesin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index e0ef5e2..75cef71 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -250,7 +250,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Није доступан ниједан уређај"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi није повезан"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветљеност"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Обрни боје"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија боја"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Још подешавања"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Корисничка подешавања"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -669,7 +669,7 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Неке функције су ограничене док се телефон не охлади.\nДодирните за више информација"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефон ће аутоматски покушати да се охлади. И даље ћете моћи да користите телефон, али ће спорије реаговати.\n\nКада се телефон охлади, нормално ће радити."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Погледајте упозорења"</string>
- <string name="high_temp_alarm_title" msgid="2359958549570161495">"Искључите пуњач из напајања"</string>
+ <string name="high_temp_alarm_title" msgid="2359958549570161495">"Искључите пуњач из струје"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Дошло је до проблема са пуњењем овог уређаја. Искључите адаптер из напајања и будите пажљиви јер кабл може да буде топао."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Погледајте упозорења"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Лева пречица"</string>
@@ -748,7 +748,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увећајте цео екран"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увећајте део екрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пређи"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Дугме Приступачност је заменило покрет за приступачност\n\n"<annotation id="link">"Прикажи подешавања"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Додирните за функције приступачности. Прилагодите или замените ово дугме у Подешавањима.\n\n"<annotation id="link">"Подешавања"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Померите дугме до ивице да бисте га привремено сакрили"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string>
@@ -883,5 +883,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Да бисте променили мрежу, прекините етернет везу"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Ради бољег доживљаја уређаја, апликације и услуге и даље могу да траже WiFi мреже у било ком тренутку, чак и када је WiFi искључен. То можете да промените у подешавањима WiFi скенирања. "<annotation id="link">"Промените"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Искључите режим рада у авиону"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изаберите корисника"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 8245be9..08041e1 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Inga tillgängliga enheter"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Ej ansluten till wifi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ljusstyrka"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Invertera färger"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Färginvertering"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Fler inställningar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Användarinställningar"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klart"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Förstora hela skärmen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Reglage"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tillgänglighetsknappen har ersatt tillgänglighetsrörelsen\n\n"<annotation id="link">"Visa inställningarna"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tryck för att öppna tillgänglighetsfunktioner. Anpassa/ersätt knappen i Inställningar.\n\n"<annotation id="link">"Inställningar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytta knappen till kanten för att dölja den tillfälligt"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytta högst upp till vänster"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytta högst upp till höger"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"I syfte att förbättra upplevelsen med enheten kan appar och tjänster fortfarande söka efter wifi-nätverk när som helst, även om wifi har inaktiverats. "<annotation id="link">"Ändra"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Inaktivera flygplansläge"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Välj användare"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index f029653..edff378 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Hakuna vifaa vilivyopatikana"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi haijaunganishwa"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ung\'avu"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Pindua rangi"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ugeuzaji rangi"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Mipangilio zaidi"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Mipangilio ya mtumiaji"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Nimemaliza"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Kuza skrini nzima"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Swichi"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Kitufe cha zana za ufikivu kimechukua nafasi ya ishara ya ufikivu\n\n"<annotation id="link">"Angalia mipangilio"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Gusa ili ufungue vipengele vya ufikivu. Weka mapendeleo au ubadilishe kitufe katika Mipangilio.\n\n"<annotation id="link">"Angalia mipangilio"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sogeza kitufe kwenye ukingo ili ukifiche kwa muda"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sogeza juu kushoto"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sogeza juu kulia"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ili kubadili mitandao, tenganisha ethaneti"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Ili kuboresha hali ya matumizi ya kifaa, programu na huduma bado zinaweza kutafuta mitandao ya Wi‑Fi wakati wowote, hata wakati umezima Wi‑Fi. Unaweza kubadilisha mipangilio hii katika mipangilio ya kutafuta Wi-Fi. "<annotation id="link">"Badilisha"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Zima hali ya ndegeni"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chagua mtumiaji"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 362e18d..dabc310 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -18,11 +18,14 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
+ <!-- The maximum number of rows in the QSPanel -->
+ <integer name="quick_settings_max_rows">3</integer>
+
<!-- The maximum number of rows in the QuickQSPanel -->
- <integer name="quick_qs_panel_max_rows">4</integer>
+ <integer name="quick_qs_panel_max_rows">3</integer>
<!-- The maximum number of tiles in the QuickQSPanel -->
- <integer name="quick_qs_panel_max_tiles">8</integer>
+ <integer name="quick_qs_panel_max_tiles">6</integer>
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">true</bool>
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
index e4573c6..e0b1614 100644
--- a/packages/SystemUI/res/values-sw720dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/config.xml
@@ -18,11 +18,14 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
+ <!-- The maximum number of rows in the QSPanel -->
+ <integer name="quick_settings_max_rows">3</integer>
+
<!-- The maximum number of rows in the QuickQSPanel -->
- <integer name="quick_qs_panel_max_rows">4</integer>
+ <integer name="quick_qs_panel_max_rows">3</integer>
<!-- The maximum number of tiles in the QuickQSPanel -->
- <integer name="quick_qs_panel_max_tiles">8</integer>
+ <integer name="quick_qs_panel_max_tiles">6</integer>
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">true</bool>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index e743b78..5b889ff5 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"சாதனங்கள் இல்லை"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"வைஃபை இணைக்கப்படவில்லை"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ஒளிர்வு"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"வண்ணங்களை மாற்று"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"கலர் இன்வெர்ஷன்"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"அமைப்பில் மாற்று"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"பயனர் அமைப்புகள்"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"முடிந்தது"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"முழுத்திரையைப் பெரிதாக்கும்"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ஸ்விட்ச்"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"அணுகல்தன்மை பட்டன் இப்போது அணுகல்தன்மை சைகையாக மாற்றப்பட்டுள்ளது\n\n"<annotation id="link">"அமைப்புகளில் காண்க"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"அணுகல்தன்மை அம்சத்தை திறக்க தட்டவும். அமைப்பில் பட்டனை பிரத்தியேகமாக்கலாம்/மாற்றலாம்.\n\n"<annotation id="link">"அமைப்பில் காண்க"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"பட்டனைத் தற்காலிகமாக மறைக்க ஓரத்திற்கு நகர்த்தும்"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"மேலே இடதுபுறத்திற்கு நகர்த்து"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"மேலே வலதுபுறத்திற்கு நகர்த்து"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"நெட்வொர்க்குகளை மாற்ற ஈதர்நெட் இணைப்பைத் துண்டிக்கவும்"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"சாதன அனுபவத்தை மேம்படுத்த, வைஃபை ஆஃப் செய்யப்பட்டிருந்தாலும்கூட எந்த நேரத்திலும் ஆப்ஸும் சேவைகளும் வைஃபை நெட்வொர்க்குகளைத் தேடலாம். வைஃபை ஸ்கேனிங் அமைப்புகளில் இதை மாற்றிக் கொள்ளலாம். "<annotation id="link">"மாற்று"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"விமானப் பயன்முறையை முடக்கும்"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"பயனரைத் தேர்வுசெய்க"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 6b26c0c..e8c30e1 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"పరికరాలు ఏవీ అందుబాటులో లేవు"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi కనెక్ట్ కాలేదు"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ప్రకాశం"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"కలర్ మార్పిడి"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"కలర్ మార్పిడి"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"మరిన్ని సెట్టింగ్లు"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"యూజర్ సెట్టింగ్లు"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"పూర్తయింది"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ఫుల్ స్క్రీన్ను మ్యాగ్నిఫై చేయండి"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్లో భాగాన్ని మాగ్నిఫై చేయండి"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"స్విచ్ చేయి"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"యాక్సెసిబిలిటీ బటన్, యాక్సెసిబిలిటీ సంజ్ఞను భర్తీ చేసింది\n\n"<annotation id="link">"సెట్టింగ్లను చూడండి"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"యాక్సెసిబిలిటీ ఫీచర్లను తెరవడానికి ట్యాప్ చేయండి. సెట్టింగ్లలో ఈ బటన్ను అనుకూలీకరించండి లేదా రీప్లేస్ చేయండి.\n\n"<annotation id="link">"వీక్షణ సెట్టింగ్లు"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"తాత్కాలికంగా దానిని దాచడానికి బటన్ను చివరకు తరలించండి"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ఎగువ ఎడమ వైపునకు తరలించు"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ఎగువ కుడి వైపునకు తరలించు"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"నెట్వర్క్లను మార్చడానికి, ఈథర్నెట్ను డిస్కనెక్ట్ చేయండి"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"పరికర అనుభవాన్ని మెరుగుపరచడానికి, Wi‑Fi ఆఫ్లో ఉన్నప్పుడు కూడా, ఏ సమయంలో అయినా ఇప్పటికీ Wi‑Fi నెట్వర్క్ల కోసం యాప్లు, సర్వీస్లు స్కాన్ చేయగలవు. మీరు దీనిని Wi‑Fi స్కానింగ్ సెట్టింగ్లలో మార్చవచ్చు. "<annotation id="link">"మార్చండి"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"విమానం మోడ్ను ఆఫ్ చేయండి"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"యూజర్ను ఎంచుకోండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 2f74a83..16672ba 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"แก้ไข"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"แก้ไขภาพหน้าจอ"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"แชร์ภาพหน้าจอ"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"จับภาพได้มากขึ้น"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ปิดภาพหน้าจอ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string>
@@ -250,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"ไม่มีอุปกรณ์ที่สามารถใช้ได้"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ความสว่าง"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"กลับสี"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"การกลับสี"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"การตั้งค่าเพิ่มเติม"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"การตั้งค่าของผู้ใช้"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"เสร็จสิ้น"</string>
@@ -744,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ขยายเป็นเต็มหน้าจอ"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"เปลี่ยน"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ปุ่มการช่วยเหลือพิเศษแทนที่ท่าทางสัมผัสการช่วยเหลือพิเศษแล้ว\n\n"<annotation id="link">"ดูการตั้งค่า"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"แตะเพื่อเปิดฟีเจอร์การช่วยเหลือพิเศษ ปรับแต่งหรือแทนที่ปุ่มนี้ในการตั้งค่า\n\n"<annotation id="link">"ดูการตั้งค่า"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ย้ายปุ่มไปที่ขอบเพื่อซ่อนชั่วคราว"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ย้ายไปด้านซ้ายบน"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ย้ายไปด้านขวาบน"</string>
@@ -878,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ตัดการเชื่อมต่ออีเทอร์เน็ตเพื่อสลับเครือข่าย"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"เพื่อปรับปรุงประสบการณ์การใช้อุปกรณ์ แอปและบริการต่างๆ จะยังคงสแกนหาเครือข่าย Wi‑Fi ได้ทุกเมื่อแม้ว่า Wi‑Fi จะปิดอยู่ คุณเปลี่ยนตัวเลือกนี้ได้ในการตั้งค่าการสแกนหา Wi-Fi "<annotation id="link">"เปลี่ยน"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ปิดโหมดบนเครื่องบิน"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"เลือกผู้ใช้"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 7b59340..9e964cb 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Hindi pinahihintulutan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"I-edit"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"I-edit ang screenshot"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Ibahagi ang screenshot"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Mag-capture pa"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"I-dismiss ang screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string>
@@ -250,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Walang available na mga device"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Hindi nakakonekta sa Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"I-invert ang mga kulay"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Pag-invert ng kulay"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Higit pang setting"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Mga setting ng user"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Tapos na"</string>
@@ -744,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"I-magnify ang buong screen"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pinalitan ng button ng accessibility ang galaw ng accessibility\n\n"<annotation id="link">"Tingnan ang mga setting"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"I-tap, buksan mga feature ng accessibility. I-customize o palitan button sa Mga Setting.\n\n"<annotation id="link">"Tingnan ang mga setting"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ilipat ang button sa gilid para pansamantala itong itago"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Ilipat sa kaliwa sa itaas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Ilipat sa kanan sa itaas"</string>
@@ -878,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para lumipat ng network, idiskonekta ang ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para pahusayin ang karanasan sa device, puwede pa ring mag-scan ng mga Wi-Fi network ang mga app at serbisyo anumang oras, kahit habang naka-off ang Wi‑Fi. Mababago mo ito sa mga setting ng pag-scan ng Wi-Fi. "<annotation id="link">"Baguhin"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"I-off ang airplane mode"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pumili ng user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 1c86eba..4e2e537 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Kullanılabilir cihaz yok"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Kablosuz ağ bağlı değil"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaklık"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Renkleri çevir"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rengi ters çevirme"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Diğer ayarlar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Kullanıcı ayarları"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Bitti"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekran büyütme"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Geç"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Erişilebilirlik hareketi, Erişilebilirlik düğmesi ile değiştirildi\n\n"<annotation id="link">"Ayarları görüntüle"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Erişilebilirlik özelliklerini açmak için dokunun. Bu düğmeyi Ayarlar\'dan özelleştirin veya değiştirin.\n\n"<annotation id="link">"Ayarları göster"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düğmeyi geçici olarak gizlemek için kenara taşıyın"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sol üste taşı"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sağ üste taşı"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ağ değiştirmek için ethernet bağlantısını kesin"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Uygulamalar ve hizmetler, cihaz deneyimini iyileştirmek için Kablosuz özelliği kapalı bile olsa kablosuz ağlar herhangi bir zamanda tarayabilir. Bunu kablosuz ağ taraması ayarlarından değiştirebilirsiniz. "<annotation id="link">"Değiştir"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Uçak modunu kapat"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kullanıcı seçin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 101ab7a..99d5e07 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Немає пристроїв"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi не під’єднано"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яскравість"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Інвертовані кольори"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія кольорів"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Більше налаштувань"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Налаштування користувача"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -753,7 +753,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Збільшення всього екрана"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Перемкнути"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Замість жесту спеціальних можливостей тепер використовується кнопка\n\n"<annotation id="link">"Переглянути налаштування"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Кнопка спеціальних можливостей. Змініть або замініть її в Налаштуваннях.\n\n"<annotation id="link">"Переглянути налаштування"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Щоб тимчасово сховати кнопку, перемістіть її на край екрана"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перемістити ліворуч угору"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перемістити праворуч угору"</string>
@@ -889,5 +889,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Щоб вибрати іншу мережу, від’єднайте кабель Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Щоб користуватися пристроєм було зручніше, додатки й сервіси можуть шукати бездротові мережі, навіть якщо Wi-Fi вимкнено. Це налаштування можна змінити в параметрах пошуку мереж Wi-Fi. "<annotation id="link">"Змінити"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Вимкнути режим польоту"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Виберіть користувача"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 824773e..109735d6 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"کوئی آلات دستیاب نہیں ہیں"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi سے منسلک نہیں ہے"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"چمکیلا پن"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"رنگ پلٹیں"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"رنگوں کی تقلیب"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"مزید ترتیبات"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"صارف کی ترتیبات"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ہو گیا"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"فُل اسکرین کو بڑا کریں"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"سوئچ کریں"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ایکسیسبیلٹی بٹن کو ایکسیسبیلٹی اشارے سے بدل دیا گیا\n\n"<annotation id="link">"ترتیبات دیکھیں"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ایکسیسبیلٹی خصوصیات کھولنے کے لیے تھپتھپائیں۔ ترتیبات میں اس بٹن کو حسب ضرورت بنائیں یا تبدیل کریں۔\n\n"<annotation id="link">"ترتیبات ملاحظہ کریں"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"عارضی طور پر بٹن کو چھپانے کے لئے اسے کنارے پر لے جائیں"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"اوپر بائیں جانب لے جائیں"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"اوپر دائیں جانب لے جائيں"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"نیٹ ورکس پر سوئچ کرنے کیلئے، ایتھرنیٹ غیر منسلک کریں"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"آلے کے تجربے کو بہتر بنانے کے لیے، Wi‑Fi کے آف ہونے پر بھی ایپس اور سروسز کسی بھی وقت Wi‑Fi نیٹ ورکس اسکین کر سکتی ہیں۔ آپ اسے Wi‑Fi اسکیننگ کی ترتیبات میں تبدیل کر سکتے ہیں۔ "<annotation id="link">"تبدیل کریں"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ہوائی جہاز وضع آف کریں"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"صارف منتخب کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index f3e637c..50e15df 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Qurilmalar topilmadi"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi tarmoqqa ulanmagan"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Yorqinlik"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Teskari ranglar"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ranglarni akslantirish"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Boshqa sozlamalar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Foydalanuvchi sozlamalari"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Tayyor"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ekranni toʻliq kattalashtirish"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Almashtirish"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Maxsus imkoniyatlar tugmasi maxsus imkoniyatlar ishorasini almashtirdi\n\n"<annotation id="link">"Sozlamalarni ochish"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Maxsus imkoniyatlarni ochish uchun bosing Sozlamalardan moslay yoki almashtira olasiz.\n\n"<annotation id="link">"Sozlamalar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Vaqtinchalik berkitish uchun tugmani qirra tomon suring"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuqori chapga surish"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuqori oʻngga surish"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Boshqa tarmoqqa almashish uchun Ethernet tarmogʻini uzing"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Qurilma ishlashini yaxshilash uchun ilova va xizmatlar hatto Wi-Fi yoqilmaganda ham istalgan vaqt Wi-Fi tarmoqlarni qidirishi mumkin. Buni taqiqlash uchun Wi-Fi tarmoqlarni qidirish funksiyasini faolsizlantiring. "<annotation id="link">"Sozlamalarni ochish"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Parvoz rejimini faolsizlantirish"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Foydalanuvchini tanlang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 7f85c06..520ea528 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Không có thiết bị nào"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Chưa kết nối với Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Độ sáng"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Đảo ngược màu"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đảo màu"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Chế độ cài đặt khác"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Cài đặt người dùng"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Xong"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Phóng to toàn màn hình"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Chuyển"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Nút hỗ trợ tiếp cận đã thay thế cử chỉ hỗ trợ tiếp cận\n\n"<annotation id="link">"Xem chế độ cài đặt"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Nhấn để mở bộ tính năng hỗ trợ tiếp cận. Tuỳ chỉnh/thay thế nút này trong phần Cài đặt.\n\n"<annotation id="link">"Xem chế độ cài đặt"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Di chuyển nút sang cạnh để ẩn nút tạm thời"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Chuyển lên trên cùng bên trái"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Chuyển lên trên cùng bên phải"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Để chuyển mạng, hãy rút cáp Ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Để cải thiện trải nghiệm khi dùng thiết bị, các ứng dụng và dịch vụ vẫn có thể quét tìm mạng Wi‑Fi bất cứ lúc nào, ngay cả khi Wi‑Fi tắt. Bạn có thể thay đổi chế độ này trong phần cài đặt tính năng Quét tìm Wi‑Fi. "<annotation id="link">"Thay đổi"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Tắt chế độ trên máy bay"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chọn người dùng"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 700e950..62d1822 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"没有可用设备"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未连接到 WLAN 网络"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"反色"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"颜色反转"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"更多设置"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"用户设置"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大整个屏幕"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分屏幕"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切换"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"“无障碍”按钮已取代无障碍手势\n\n"<annotation id="link">"查看设置"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"点按即可打开无障碍功能。您可在“设置”中自定义或更换此按钮。\n\n"<annotation id="link">"查看设置"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"将按钮移到边缘,即可暂时将其隐藏"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移至左上角"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移至右上角"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切换网络,请断开以太网连接"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"为了提升设备的使用体验,即使 WLAN 已关闭,应用和服务仍可以随时扫描 WLAN 网络。您可以在 WLAN 扫描设置中更改此设置。"<annotation id="link">"更改"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"关闭飞行模式"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"选择用户"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index bea00c7..2273b6d 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"沒有可用裝置"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未連線至 Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"反轉顏色"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"更多設定"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"使用者設定"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大成個畫面"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"無障礙功能按鈕已取代無障礙手勢\n\n"<annotation id="link">"查看設定"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"㩒一下就可以開無障礙功能。喺「設定」度自訂或者取代呢個按鈕。\n\n"<annotation id="link">"查看設定"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣即可暫時隱藏"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移去左上方"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移去右上方"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網絡,請中斷以太網連線"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"為改善裝置的使用體驗,應用程式和服務仍可隨時掃瞄 Wi-Fi 網絡 (即使 Wi-Fi 已關閉)。您可在 Wi-Fi 掃瞄設定中變更此設定。"<annotation id="link">"變更"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"關閉飛行模式"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index c450f9d..4b0340f 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"沒有可用裝置"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未連線至 Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"反轉顏色"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"更多設定"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"使用者設定"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大整個螢幕畫面"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大局部螢幕畫面"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"無障礙工具按鈕已取代無障礙手勢\n\n"<annotation id="link">"查看設定"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"輕觸即可開啟無障礙功能。你可以前往「設定」自訂或更換這個按鈕。\n\n"<annotation id="link">"查看設定"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣處即可暫時隱藏"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移到左上方"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移到右上方"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網路,請中斷乙太網路連線"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"為提升裝置的使用體驗,應用程式和服務仍可隨時掃描 Wi‑Fi 網路,即使 Wi-Fi 連線功能處於關閉狀態時亦然。你可以前往「掃描 Wi-Fi」設定進行變更。"<annotation id="link">"變更"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"關閉飛航模式"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 0f9bde5a..bffb248 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -249,7 +249,7 @@
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Ayikho idivayisi etholakalayo"</string>
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"I-Wi-Fi ayixhunyiwe"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ukugqama"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Faka imibala"</string>
+ <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ukuguqulwa kombala"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Izilungiselelo eziningi"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Amasethingi womsebenzisi"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Kwenziwe"</string>
@@ -743,7 +743,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Khulisa isikrini esigcwele"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Iswishi"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Inkinobho yokufinyeleleka ishintshaniswe ngokuthinta kokufinyeleleka\n\n"<annotation id="link">"Buka amasethingi"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Thepha ukuze uvule izakhi zokufinyelela. Enza ngendlela oyifisayo noma shintsha le nkinobho Kumasethingi.\n\n"<annotation id="link">"Buka amasethingi"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Hambisa inkinobho onqenqemeni ukuze uyifihle okwesikhashana"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Hamba phezulu kwesokunxele"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Hamba phezulu ngakwesokudla"</string>
@@ -877,5 +877,6 @@
<string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ukuze ushintshe amanethiwekhi, nqamula i-ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Ukuze kuthuthukiswe ukuzizwela kwedivayisi, ama-app namasevisi kusengakwazi ukuskena amanethiwekhi we-Wi-Fi noma kunini, ngisho noma i-Wi-Fi ivaliwe, Ungashintsha lokhu kumasethingi Wokuskena i-Wi-Fi. "<annotation id="link">"Shintsha"</annotation></string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Vala imodi yendiza"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Khetha umsebenzisi"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8a3baca..e455aaa 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -134,10 +134,10 @@
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
<!-- UDFPS colors -->
- <color name="udfps_enroll_icon">#000000</color> <!-- 100% black -->
- <color name="udfps_moving_target_fill">#cc4285f4</color> <!-- 80% blue -->
- <color name="udfps_enroll_progress">#ff669DF6</color> <!-- blue 400 -->
- <color name="udfps_enroll_progress_help">#ffEE675C</color> <!-- red 400 -->
+ <color name="udfps_enroll_icon">#7DA7F1</color>
+ <color name="udfps_moving_target_fill">#475670</color>
+ <color name="udfps_enroll_progress">#7DA7F1</color>
+ <color name="udfps_enroll_progress_help">#ffEE675C</color>
<!-- Global screenshot actions -->
<color name="global_screenshot_button_ripple">#1f000000</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2afbe8c6..4c4a3bb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -601,7 +601,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_margin">-52dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
<!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
<item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
@@ -1243,6 +1243,8 @@
<!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_max_height">662dp</dimen>
+ <!-- The height of the WiFi network in Internet panel. -->
+ <dimen name="internet_dialog_wifi_network_height">72dp</dimen>
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">@dimen/match_parent</dimen>
@@ -1252,6 +1254,8 @@
<!-- Internet dialog related dimensions -->
<dimen name="internet_dialog_corner_radius">24dp</dimen>
+ <!-- Width of progress bar -->
+ <dimen name="internet_dialog_progress_bar_width">152dp</dimen>
<!-- End margin of network layout -->
<dimen name="internet_dialog_network_layout_margin">16dp</dimen>
<!-- Size of switch bar in internet dialog -->
@@ -1275,10 +1279,19 @@
<dimen name="drag_and_drop_icon_size">70dp</dimen>
- <dimen name="qs_dialog_button_horizontal_padding">16dp</dimen>
- <dimen name="qs_dialog_button_vertical_padding">8dp</dimen>
+ <!-- Dimensions for unified SystemUI dialogs styling. Used by Theme.SystemUI.Dialog and
+ alert_dialog_systemui.xml
+ -->
+ <dimen name="dialog_button_horizontal_padding">16dp</dimen>
+ <dimen name="dialog_button_vertical_padding">8dp</dimen>
<!-- The button will be 48dp tall, but the background needs to be 36dp tall -->
- <dimen name="qs_dialog_button_vertical_inset">6dp</dimen>
+ <dimen name="dialog_button_vertical_inset">6dp</dimen>
+ <dimen name="dialog_top_padding">24dp</dimen>
+ <dimen name="dialog_bottom_padding">18dp</dimen>
+ <dimen name="dialog_side_padding">24dp</dimen>
+ <dimen name="dialog_button_bar_top_padding">32dp</dimen>
+
+ <!-- ************************************************************************* -->
<dimen name="keyguard_unfold_translation_x">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8797266..e1afd3f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -630,7 +630,7 @@
<!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] -->
<string name="quick_settings_brightness_dialog_title">Brightness</string>
<!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_inversion_label">Invert colors</string>
+ <string name="quick_settings_inversion_label">Color inversion</string>
<!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] -->
<string name="quick_settings_more_settings">More settings</string>
@@ -2037,8 +2037,8 @@
<string name="magnification_mode_switch_click_label">Switch</string>
<!-- Accessibility floating menu strings -->
- <!-- Message for the accessibility floating button migration tooltip. It shows when the user use gestural navigation then upgrade their system. It will tell the user the accessibility gesture had been replaced by accessibility floating button. [CHAR LIMIT=100] -->
- <string name="accessibility_floating_button_migration_tooltip">Accessibility button replaced the accessibility gesture\n\n<annotation id="link">View settings</annotation></string>
+ <!-- Message for the accessibility floating button migration tooltip. It shows when the user use gestural navigation then upgrade their system. It will tell the user they could customize or replace the floating button in Settings. [CHAR LIMIT=100] -->
+ <string name="accessibility_floating_button_migration_tooltip">Tap to open accessibility features. Customize or replace this button in Settings.\n\n<annotation id="link">View settings</annotation></string>
<!-- Message for the accessibility floating button docking tooltip. It shows when the user first time drag the button. It will tell the user about docking behavior. [CHAR LIMIT=70] -->
<string name="accessibility_floating_button_docking_tooltip">Move button to the edge to hide it temporarily</string>
<!-- Action in accessibility menu to move the accessibility floating button to the top left of the screen. [CHAR LIMIT=30] -->
@@ -2336,6 +2336,8 @@
<string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
<!-- Message to describe "Wi-Fi scan always available feature" when Wi-Fi is off and Wi-Fi scanning is on. [CHAR LIMIT=NONE] -->
<string name="wifi_scan_notify_message">To improve device experience, apps and services can still scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. You can change this in Wi\u2011Fi scanning settings. <annotation id="link">Change</annotation></string>
+ <!-- Provider Model: Description of the airplane mode button. [CHAR LIMIT=60] -->
+ <string name="turn_off_airplane_mode">Turn off airplane mode</string>
<!-- Title for User Switch dialog. [CHAR LIMIT=20] -->
<string name="qs_user_switch_dialog_title">Select user</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 94d10de..051a30c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- NOTE: Adding the androidprv: namespace to this file will break the studio build. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- HybridNotification themes and styles -->
@@ -351,11 +351,19 @@
<item name="android:windowIsFloating">true</item>
</style>
- <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
+ <style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog"/>
+
+ <style name="Theme.SystemUI.Dialog" parent="@style/Theme.SystemUI.DayNightDialog">
<item name="android:buttonCornerRadius">28dp</item>
- <item name="android:buttonBarPositiveButtonStyle">@style/Widget.QSDialog.Button</item>
- <item name="android:buttonBarNegativeButtonStyle">@style/Widget.QSDialog.Button.BorderButton</item>
- <item name="android:buttonBarNeutralButtonStyle">@style/Widget.QSDialog.Button.BorderButton</item>
+ <item name="android:buttonBarPositiveButtonStyle">@style/Widget.Dialog.Button</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
+ <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
+ <item name="android:colorBackground">?androidprv:attr/colorSurface</item>
+ <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
+ </style>
+
+ <style name="AlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault">
+ <item name="android:layout">@layout/alert_dialog_systemui</item>
</style>
<style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -365,11 +373,11 @@
<item name="android:windowIsFloating">true</item>
</style>
- <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
- <item name="android:windowIsFloating">true</item>
+ <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="Theme.SystemUI.Dialog">
+ <!-- Settings windowFullscreen: true is necessary to be able to intercept touch events -->
+ <!-- that would otherwise be intercepted by the Shade. -->
+ <item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:backgroundDimEnabled">true</item>
- <item name="android:windowCloseOnTouchOutside">true</item>
</style>
<style name="QSBorderlessButton">
@@ -442,10 +450,6 @@
<item name="android:background">@drawable/btn_borderless_rect</item>
</style>
- <style name="MediaOutputRoundedOutlinedButton" parent="@android:style/Widget.Material.Button">
- <item name="android:background">@drawable/media_output_dialog_button_background</item>
- </style>
-
<style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:windowActionBar">false</item>
<item name="preferenceTheme">@style/TunerPreferenceTheme</item>
@@ -829,7 +833,6 @@
<style name="Widget.SliceView.Panel">
<item name="titleSize">16sp</item>
<item name="rowStyle">@style/SliceRow</item>
- <item name="android:background">?android:attr/colorBackgroundFloating</item>
</style>
<style name="SliceRow">
@@ -853,24 +856,42 @@
<item name="actionDividerHeight">32dp</item>
</style>
- <style name="TextAppearance.QSDialog.Title" parent="Theme.SystemUI.Dialog">
+ <style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">24sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:lineHeight">32sp</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textAlignment">center</item>
</style>
- <style name="Widget.QSDialog.Button" parent = "Theme.SystemUI.Dialog">
+ <style name="TextAppearance.Dialog.Body" parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:lineHeight">20sp</item>
+ </style>
+
+ <style name="TextAppearance.Dialog.Body.Message">
+ <item name="android:gravity">center</item>
+ <item name="android:textAlignment">center</item>
+ </style>
+
+
+ <style name="Widget" />
+ <style name="Widget.Dialog" />
+ <style name="Widget.Dialog.Button">
+ <item name="android:buttonCornerRadius">28dp</item>
<item name="android:background">@drawable/qs_dialog_btn_filled</item>
- <item name="android:textColor">@color/prv_text_color_on_accent</item>
+ <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:stateListAnimator">@null</item>
- <item name="android:layout_marginHorizontal">4dp</item>
+ <item name="android:minWidth">0dp</item>
</style>
- <style name="Widget.QSDialog.Button.BorderButton">
+ <style name="Widget.Dialog.Button.BorderButton">
<item name="android:background">@drawable/qs_dialog_btn_outline</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
@@ -947,4 +968,10 @@
<style name="TextAppearance.InternetDialog.Secondary.Active"/>
+ <style name="InternetDialog.Divider">
+ <item name="android:background">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="InternetDialog.Divider.Active"/>
+
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
index 07ad0c8..8aa3aba 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
@@ -51,6 +51,9 @@
private static final Interpolator ALPHA_OUT_INTERPOLATOR =
new PathInterpolator(0f, 0f, 0.8f, 1f);
+ @DimenRes
+ private final int mMaxWidthResource;
+
private Paint mRipplePaint;
private CanvasProperty<Float> mLeftProp;
private CanvasProperty<Float> mTopProp;
@@ -90,10 +93,17 @@
private Type mType = Type.ROUNDED_RECT;
public KeyButtonRipple(Context ctx, View targetView, @DimenRes int maxWidthResource) {
+ mMaxWidthResource = maxWidthResource;
mMaxWidth = ctx.getResources().getDimensionPixelSize(maxWidthResource);
mTargetView = targetView;
}
+ public void updateResources() {
+ mMaxWidth = mTargetView.getContext().getResources()
+ .getDimensionPixelSize(mMaxWidthResource);
+ invalidateSelf();
+ }
+
public void setDarkIntensity(float darkIntensity) {
mDark = darkIntensity >= 0.5f;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 7d0fb5d..567e7aa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -53,8 +53,8 @@
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
.setPosition(leash, positionX, positionY)
.setCornerRadius(leash, cornerRadius);
- return new PictureInPictureSurfaceTransaction(
- positionX, positionY, mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
+ return newPipSurfaceTransaction(positionX, positionY,
+ mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
}
public PictureInPictureSurfaceTransaction scale(
@@ -70,8 +70,8 @@
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
.setPosition(leash, positionX, positionY)
.setCornerRadius(leash, cornerRadius);
- return new PictureInPictureSurfaceTransaction(
- positionX, positionY, mTmpFloat9, degree, cornerRadius, sourceBounds);
+ return newPipSurfaceTransaction(positionX, positionY,
+ mTmpFloat9, degree, cornerRadius, sourceBounds);
}
public PictureInPictureSurfaceTransaction scaleAndCrop(
@@ -93,8 +93,8 @@
.setWindowCrop(leash, mTmpDestinationRect)
.setPosition(leash, left, top)
.setCornerRadius(leash, cornerRadius);
- return new PictureInPictureSurfaceTransaction(
- left, top, mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
+ return newPipSurfaceTransaction(left, top,
+ mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
}
public PictureInPictureSurfaceTransaction scaleAndRotate(
@@ -125,8 +125,7 @@
.setWindowCrop(leash, mTmpDestinationRect)
.setPosition(leash, adjustedPositionX, adjustedPositionY)
.setCornerRadius(leash, cornerRadius);
- return new PictureInPictureSurfaceTransaction(
- adjustedPositionX, adjustedPositionY,
+ return newPipSurfaceTransaction(adjustedPositionX, adjustedPositionY,
mTmpFloat9, degree, cornerRadius, mTmpDestinationRect);
}
@@ -137,6 +136,17 @@
return mCornerRadius * scale;
}
+ private static PictureInPictureSurfaceTransaction newPipSurfaceTransaction(
+ float posX, float posY, float[] float9, float rotation, float cornerRadius,
+ Rect windowCrop) {
+ return new PictureInPictureSurfaceTransaction.Builder()
+ .setPosition(posX, posY)
+ .setTransform(float9, rotation)
+ .setCornerRadius(cornerRadius)
+ .setWindowCrop(windowCrop)
+ .build();
+ }
+
/** @return {@link SurfaceControl.Transaction} instance with vsync-id */
public static SurfaceControl.Transaction newSurfaceControlTransaction() {
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 8bd0f91..0149751 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -95,4 +95,9 @@
* Sent when screen turned on and ready to use (blocker scrim is hidden)
*/
void onScreenTurnedOn() = 21;
+
+ /**
+ * Sent when the desired dark intensity of the nav buttons has changed
+ */
+ void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 3128ffd..675dc9b5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -28,6 +28,7 @@
import android.os.Parcelable;
import android.view.ViewDebug;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.PrintWriter;
@@ -50,6 +51,7 @@
@ViewDebug.ExportedProperty(category="recents")
public int windowingMode;
@ViewDebug.ExportedProperty(category="recents")
+ @NonNull
public final Intent baseIntent;
@ViewDebug.ExportedProperty(category="recents")
public final int userId;
@@ -83,7 +85,7 @@
updateHashCode();
}
- public TaskKey(int id, int windowingMode, Intent intent,
+ public TaskKey(int id, int windowingMode, @NonNull Intent intent,
ComponentName sourceComponent, int userId, long lastActiveTime) {
this.id = id;
this.windowingMode = windowingMode;
@@ -95,7 +97,7 @@
updateHashCode();
}
- public TaskKey(int id, int windowingMode, Intent intent,
+ public TaskKey(int id, int windowingMode, @NonNull Intent intent,
ComponentName sourceComponent, int userId, long lastActiveTime, int displayId) {
this.id = id;
this.windowingMode = windowingMode;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 6154d84..8d98a75 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -18,7 +18,6 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import android.annotation.TargetApi;
import android.content.Context;
@@ -126,10 +125,9 @@
final WindowManager windowManager = context.getSystemService(WindowManager.class);
final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
- float originalSmallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
+ float smallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
context.getResources().getConfiguration().densityDpi);
- return dpiFromPx(Math.min(bounds.width(), bounds.height()), DENSITY_DEVICE_STABLE)
- >= TABLET_MIN_DPS && originalSmallestWidth >= TABLET_MIN_DPS;
+ return smallestWidth >= TABLET_MIN_DPS;
}
public static float dpiFromPx(float size, int densityDpi) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index cbf7397..857cc462 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -21,6 +21,8 @@
import android.annotation.LayoutRes;
import android.annotation.StringRes;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -29,12 +31,12 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import androidx.core.view.OneShotPreDrawListener;
-import com.android.systemui.shared.R;
import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
/**
@@ -48,7 +50,21 @@
private final ViewGroup mKeyButtonContainer;
private final FloatingRotationButtonView mKeyButtonView;
- private final int mContainerSize;
+ private int mContainerSize;
+ private final Context mContext;
+
+ @StringRes
+ private final int mContentDescriptionResource;
+ @DimenRes
+ private final int mMinMarginResource;
+ @DimenRes
+ private final int mRoundedContentPaddingResource;
+ @DimenRes
+ private final int mTaskbarLeftMarginResource;
+ @DimenRes
+ private final int mTaskbarBottomMarginResource;
+ @DimenRes
+ private final int mButtonDiameterResource;
private AnimatedVectorDrawable mAnimatedDrawable;
private boolean mIsShowing;
@@ -58,13 +74,13 @@
private boolean mIsTaskbarVisible = false;
private boolean mIsTaskbarStashed = false;
- private final FloatingRotationButtonPositionCalculator mPositionCalculator;
+ private FloatingRotationButtonPositionCalculator mPositionCalculator;
private RotationButtonController mRotationButtonController;
private RotationButtonUpdatesCallback mUpdatesCallback;
private Position mPosition;
- public FloatingRotationButton(Context context, @StringRes int contentDescription,
+ public FloatingRotationButton(Context context, @StringRes int contentDescriptionResource,
@LayoutRes int layout, @IdRes int keyButtonId, @DimenRes int minMargin,
@DimenRes int roundedContentPadding, @DimenRes int taskbarLeftMargin,
@DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
@@ -73,24 +89,37 @@
mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(layout, null);
mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
mKeyButtonView.setVisibility(View.VISIBLE);
- mKeyButtonView.setContentDescription(context.getString(contentDescription));
+ mKeyButtonView.setContentDescription(context.getString(contentDescriptionResource));
mKeyButtonView.setRipple(rippleMaxWidth);
- Resources res = context.getResources();
+ mContext = context;
+
+ mContentDescriptionResource = contentDescriptionResource;
+ mMinMarginResource = minMargin;
+ mRoundedContentPaddingResource = roundedContentPadding;
+ mTaskbarLeftMarginResource = taskbarLeftMargin;
+ mTaskbarBottomMarginResource = taskbarBottomMargin;
+ mButtonDiameterResource = buttonDiameter;
+
+ updateDimensionResources();
+ }
+
+ private void updateDimensionResources() {
+ Resources res = mContext.getResources();
int defaultMargin = Math.max(
- res.getDimensionPixelSize(minMargin),
- res.getDimensionPixelSize(roundedContentPadding));
+ res.getDimensionPixelSize(mMinMarginResource),
+ res.getDimensionPixelSize(mRoundedContentPaddingResource));
int taskbarMarginLeft =
- res.getDimensionPixelSize(taskbarLeftMargin);
+ res.getDimensionPixelSize(mTaskbarLeftMarginResource);
int taskbarMarginBottom =
- res.getDimensionPixelSize(taskbarBottomMargin);
+ res.getDimensionPixelSize(mTaskbarBottomMarginResource);
mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
taskbarMarginLeft, taskbarMarginBottom);
- final int diameter = res.getDimensionPixelSize(buttonDiameter);
+ final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
taskbarMarginBottom));
}
@@ -119,32 +148,10 @@
}
mIsShowing = true;
- int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- // TODO(b/200103245): add new window type that has z-index above
- // TYPE_NAVIGATION_BAR_PANEL as currently it could be below the taskbar which has
- // the same window type
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- mContainerSize,
- mContainerSize,
- 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
- PixelFormat.TRANSLUCENT);
+ final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
+ mWindowManager.addView(mKeyButtonContainer, layoutParams);
- lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- lp.setTitle("FloatingRotationButton");
- lp.setFitInsetsTypes(0 /*types */);
-
- mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
- mPosition = mPositionCalculator
- .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
-
- lp.gravity = mPosition.getGravity();
- ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
- mPosition.getGravity();
-
- updateTranslation(mPosition, /* animate */ false);
-
- mWindowManager.addView(mKeyButtonContainer, lp);
if (mAnimatedDrawable != null) {
mAnimatedDrawable.reset();
mAnimatedDrawable.start();
@@ -232,6 +239,53 @@
}
}
+ /**
+ * Updates resources that could be changed in runtime, should be called on configuration
+ * change with changes diff integer mask
+ * @param configurationChanges - configuration changes with flags from ActivityInfo e.g.
+ * {@link android.content.pm.ActivityInfo#CONFIG_DENSITY}
+ */
+ public void onConfigurationChanged(@Config int configurationChanges) {
+ if ((configurationChanges & ActivityInfo.CONFIG_DENSITY) != 0
+ || (configurationChanges & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+ updateDimensionResources();
+
+ if (mIsShowing) {
+ final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
+ mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
+ }
+ }
+
+ if ((configurationChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+ mKeyButtonView.setContentDescription(mContext.getString(mContentDescriptionResource));
+ }
+ }
+
+ private LayoutParams adjustViewPositionAndCreateLayoutParams() {
+ final LayoutParams lp = new LayoutParams(
+ mContainerSize,
+ mContainerSize,
+ /* xpos */ 0, /* ypos */ 0, LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+
+ lp.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setTitle("FloatingRotationButton");
+ lp.setFitInsetsTypes(/* types */ 0);
+
+ mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
+ mPosition = mPositionCalculator
+ .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+ lp.gravity = mPosition.getGravity();
+ ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
+ mPosition.getGravity();
+
+ updateTranslation(mPosition, /* animate */ false);
+
+ return lp;
+ }
+
private void updateTranslation(Position position, boolean animate) {
final int translationX = position.getTranslationX();
final int translationY = position.getTranslationY();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index c5f8fc1..a4b6451 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -17,6 +17,8 @@
package com.android.systemui.shared.rotation;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -37,12 +39,15 @@
private KeyButtonRipple mRipple;
private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ private final Configuration mLastConfiguration;
+
public FloatingRotationButtonView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingRotationButtonView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mLastConfiguration = getResources().getConfiguration();
setClickable(true);
@@ -63,6 +68,17 @@
}
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ final int changes = mLastConfiguration.updateFrom(newConfig);
+ if ((changes & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
+ || ((changes & ActivityInfo.CONFIG_DENSITY) != 0)) {
+ if (mRipple != null) {
+ mRipple.updateResources();
+ }
+ }
+ }
+
public void setColors(int lightColor, int darkColor) {
getDrawable().setColorFilter(new PorterDuffColorFilter(lightColor, PorterDuff.Mode.SRC_IN));
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 2dbd5de..605d376 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -36,6 +36,7 @@
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
import android.view.IRotationWatcher;
import android.view.MotionEvent;
import android.view.Surface;
@@ -453,6 +454,7 @@
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
incrementNumAcceptedRotationSuggestionsIfNeeded();
setRotationLockedAtAngle(mLastRotationSuggestion);
+ v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
private boolean onRotateSuggestionHover(View v, MotionEvent event) {
@@ -481,7 +483,9 @@
* orientation overview.
*/
public void setSkipOverrideUserLockPrefsOnce() {
- mSkipOverrideUserLockPrefsOnce = true;
+ // If live-tile is enabled (recents animation keeps running in overview), there is no
+ // activity switch so the display rotation is not changed, then it is no need to skip.
+ mSkipOverrideUserLockPrefsOnce = !mIsRecentsAnimationRunning;
}
private boolean shouldOverrideUserLockPrefs(final int rotation) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 38eded8..48fcbbd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -61,7 +61,7 @@
public class ActivityManagerWrapper {
private static final String TAG = "ActivityManagerWrapper";
-
+ private static final int NUM_RECENT_ACTIVITIES_REQUEST = 3;
private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
// Should match the values in PhoneWindowManager
@@ -113,6 +113,22 @@
}
/**
+ * We ask for {@link #NUM_RECENT_ACTIVITIES_REQUEST} activities because when in split screen,
+ * we'll get back 2 activities for each split app and one for launcher. Launcher might be more
+ * "recently" used than one of the split apps so if we only request 2 tasks, then we might miss
+ * out on one of the split apps
+ *
+ * @return an array of up to {@link #NUM_RECENT_ACTIVITIES_REQUEST} running tasks
+ * filtering only for tasks that can be visible in the recent tasks list.
+ */
+ public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) {
+ // Note: The set of running tasks from the system is ordered by recency
+ List<ActivityManager.RunningTaskInfo> tasks =
+ mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents);
+ return tasks.toArray(new RunningTaskInfo[tasks.size()]);
+ }
+
+ /**
* @return a list of the recents tasks.
*/
public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 30db136..2583e09 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -278,7 +278,9 @@
* @see SurfaceControl#release()
*/
public void release() {
- leash.mSurfaceControl.release();
+ if (leash.mSurfaceControl != null) {
+ leash.mSurfaceControl.release();
+ }
if (mStartLeash != null) {
mStartLeash.release();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index b6be6ed..ea93a3b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -26,6 +26,7 @@
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
@@ -59,17 +60,19 @@
hingeAngleProvider,
screenStatusProvider,
deviceStateManager,
- mainExecutor
+ mainExecutor,
+ mainHandler
)
- return if (config.isHingeAngleEnabled) {
- PhysicsBasedUnfoldTransitionProgressProvider(
- mainHandler,
- foldStateProvider
- )
+ val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
+ PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
} else {
FixedTimingTransitionProgressProvider(foldStateProvider)
}
+ return ScaleAwareTransitionProgressProvider(
+ unfoldTransitionProgressProvider,
+ context.contentResolver
+ )
}
fun createConfig(context: Context): UnfoldTransitionConfig =
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index 90f5998..51eae57 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.unfold.progress
-import android.os.Handler
import android.util.Log
import android.util.MathUtils.saturate
import androidx.dynamicanimation.animation.DynamicAnimation
@@ -24,9 +23,10 @@
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_ABORTED
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
-import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
@@ -39,7 +39,6 @@
* - doesn't handle postures
*/
internal class PhysicsBasedUnfoldTransitionProgressProvider(
- private val handler: Handler,
private val foldStateProvider: FoldStateProvider
) :
UnfoldTransitionProgressProvider,
@@ -51,8 +50,6 @@
addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider)
}
- private val timeoutRunnable = TimeoutRunnable()
-
private var isTransitionRunning = false
private var isAnimatedCancelRunning = false
@@ -92,7 +89,7 @@
cancelTransition(endValue = 1f, animate = true)
}
}
- FOLD_UPDATE_FINISH_FULL_OPEN -> {
+ FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_ABORTED -> {
// Do not cancel if we haven't started the transition yet.
// This could happen when we fully unfolded the device before the screen
// became available. In this case we start and immediately cancel the animation
@@ -106,7 +103,11 @@
cancelTransition(endValue = 0f, animate = false)
}
FOLD_UPDATE_START_CLOSING -> {
- startTransition(startValue = 1f)
+ // The transition might be already running as the device might start closing several
+ // times before reaching an end state.
+ if (!isTransitionRunning) {
+ startTransition(startValue = 1f)
+ }
}
}
@@ -116,8 +117,6 @@
}
private fun cancelTransition(endValue: Float, animate: Boolean) {
- handler.removeCallbacks(timeoutRunnable)
-
if (isTransitionRunning && animate) {
isAnimatedCancelRunning = true
springAnimation.animateToFinalPosition(endValue)
@@ -175,8 +174,6 @@
}
springAnimation.start()
-
- handler.postDelayed(timeoutRunnable, TRANSITION_TIMEOUT_MILLIS)
}
override fun addCallback(listener: TransitionProgressListener) {
@@ -187,13 +184,6 @@
listeners.remove(listener)
}
- private inner class TimeoutRunnable : Runnable {
-
- override fun run() {
- cancelTransition(endValue = 1f, animate = true)
- }
- }
-
private object AnimationProgressProperty :
FloatPropertyCompat<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") {
@@ -212,7 +202,6 @@
private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider"
private const val DEBUG = true
-private const val TRANSITION_TIMEOUT_MILLIS = 2000L
private const val SPRING_STIFFNESS = 200.0f
private const val MINIMAL_VISIBLE_CHANGE = 0.001f
private const val FINAL_HINGE_ANGLE_POSITION = 165f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 35e2b30..6d9631c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -15,14 +15,19 @@
*/
package com.android.systemui.unfold.updates
+import android.annotation.FloatRange
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.util.Log
+import androidx.annotation.VisibleForTesting
import androidx.core.util.Consumer
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import java.util.concurrent.Executor
class DeviceFoldStateProvider(
@@ -30,7 +35,8 @@
private val hingeAngleProvider: HingeAngleProvider,
private val screenStatusProvider: ScreenStatusProvider,
private val deviceStateManager: DeviceStateManager,
- private val mainExecutor: Executor
+ private val mainExecutor: Executor,
+ private val handler: Handler
) : FoldStateProvider {
private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
@@ -38,9 +44,13 @@
@FoldUpdate
private var lastFoldUpdate: Int? = null
+ @FloatRange(from = 0.0, to = 180.0)
+ private var lastHingeAngle: Float = 0f
+
private val hingeAngleListener = HingeAngleListener()
private val screenListener = ScreenStatusListener()
private val foldStateListener = FoldStateListener(context)
+ private val timeoutRunnable = TimeoutRunnable()
private var isFolded = false
private var isUnfoldHandled = true
@@ -72,47 +82,69 @@
override val isFullyOpened: Boolean
get() = !isFolded && lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN
+ private val isTransitionInProgess: Boolean
+ get() = lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
+ lastFoldUpdate == FOLD_UPDATE_START_CLOSING
+
private fun onHingeAngle(angle: Float) {
- when (lastFoldUpdate) {
- FOLD_UPDATE_FINISH_FULL_OPEN -> {
- if (FULLY_OPEN_DEGREES - angle > START_CLOSING_THRESHOLD_DEGREES) {
- lastFoldUpdate = FOLD_UPDATE_START_CLOSING
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
- }
- }
- FOLD_UPDATE_START_OPENING -> {
- if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
- lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
- }
- }
- FOLD_UPDATE_START_CLOSING -> {
- if (FULLY_OPEN_DEGREES - angle < START_CLOSING_THRESHOLD_DEGREES) {
- lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
- }
+ if (DEBUG) { Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle") }
+
+ val isClosing = angle < lastHingeAngle
+ val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
+ val closingEventDispatched = lastFoldUpdate == FOLD_UPDATE_START_CLOSING
+
+ if (isClosing && !closingEventDispatched && !isFullyOpened) {
+ notifyFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ }
+
+ if (isTransitionInProgess) {
+ if (isFullyOpened) {
+ notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+ cancelTimeout()
+ } else {
+ // The timeout will trigger some constant time after the last angle update.
+ rescheduleAbortAnimationTimeout()
}
}
+ lastHingeAngle = angle
outputListeners.forEach { it.onHingeAngleUpdate(angle) }
}
private inner class FoldStateListener(context: Context) :
DeviceStateManager.FoldStateListener(context, { folded: Boolean ->
isFolded = folded
+ lastHingeAngle = FULLY_CLOSED_DEGREES
if (folded) {
- lastFoldUpdate = FOLD_UPDATE_FINISH_CLOSED
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) }
hingeAngleProvider.stop()
+ notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+ cancelTimeout()
isUnfoldHandled = false
} else {
- lastFoldUpdate = FOLD_UPDATE_START_OPENING
- outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_OPENING) }
+ notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
+ rescheduleAbortAnimationTimeout()
hingeAngleProvider.start()
}
})
+ private fun notifyFoldUpdate(@FoldUpdate update: Int) {
+ if (DEBUG) { Log.d(TAG, stateToString(update)) }
+ outputListeners.forEach { it.onFoldUpdate(update) }
+ lastFoldUpdate = update
+ }
+
+ private fun rescheduleAbortAnimationTimeout() {
+ if (isTransitionInProgess) {
+ cancelTimeout()
+ }
+ handler.postDelayed(timeoutRunnable, ABORT_CLOSING_MILLIS)
+ }
+
+ private fun cancelTimeout() {
+ handler.removeCallbacks(timeoutRunnable)
+ }
+
private inner class ScreenStatusListener :
ScreenStatusProvider.ScreenListener {
@@ -136,7 +168,39 @@
onHingeAngle(angle)
}
}
+
+ private inner class TimeoutRunnable : Runnable {
+
+ override fun run() {
+ notifyFoldUpdate(FOLD_UPDATE_ABORTED)
+ }
+ }
}
-private const val START_CLOSING_THRESHOLD_DEGREES = 95f
-private const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
+private fun stateToString(@FoldUpdate update: Int): String {
+ return when (update) {
+ FOLD_UPDATE_START_OPENING -> "START_OPENING"
+ FOLD_UPDATE_HALF_OPEN -> "HALF_OPEN"
+ FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
+ FOLD_UPDATE_ABORTED -> "ABORTED"
+ FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
+ FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN"
+ FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN"
+ FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED"
+ else -> "UNKNOWN"
+ }
+}
+
+private const val TAG = "DeviceFoldProvider"
+private const val DEBUG = false
+
+/**
+ * Time after which [FOLD_UPDATE_ABORTED] is emitted following a [FOLD_UPDATE_START_CLOSING] or
+ * [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
+ */
+@VisibleForTesting
+const val ABORT_CLOSING_MILLIS = 1000L
+
+/** Threshold after which we consider the device fully unfolded. */
+@VisibleForTesting
+const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 643ece3..bffebcd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -39,6 +39,7 @@
FOLD_UPDATE_START_OPENING,
FOLD_UPDATE_HALF_OPEN,
FOLD_UPDATE_START_CLOSING,
+ FOLD_UPDATE_ABORTED,
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
FOLD_UPDATE_FINISH_HALF_OPEN,
FOLD_UPDATE_FINISH_FULL_OPEN,
@@ -51,7 +52,8 @@
const val FOLD_UPDATE_START_OPENING = 0
const val FOLD_UPDATE_HALF_OPEN = 1
const val FOLD_UPDATE_START_CLOSING = 2
-const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 3
-const val FOLD_UPDATE_FINISH_HALF_OPEN = 4
-const val FOLD_UPDATE_FINISH_FULL_OPEN = 5
-const val FOLD_UPDATE_FINISH_CLOSED = 6
+const val FOLD_UPDATE_ABORTED = 3
+const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 4
+const val FOLD_UPDATE_FINISH_HALF_OPEN = 5
+const val FOLD_UPDATE_FINISH_FULL_OPEN = 6
+const val FOLD_UPDATE_FINISH_CLOSED = 7
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
index e072d41..58d7dfb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
@@ -2,7 +2,6 @@
import android.content.Context
import android.os.RemoteException
-import android.util.Log
import android.view.IRotationWatcher
import android.view.IWindowManager
import android.view.Surface
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
new file mode 100644
index 0000000..df9078a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -0,0 +1,50 @@
+package com.android.systemui.unfold.util
+
+import android.animation.ValueAnimator
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+
+/** Wraps [UnfoldTransitionProgressProvider] to disable transitions when animations are disabled. */
+class ScaleAwareTransitionProgressProvider(
+ unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+ private val contentResolver: ContentResolver
+) : UnfoldTransitionProgressProvider {
+
+ private val scopedUnfoldTransitionProgressProvider =
+ ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+
+ private val animatorDurationScaleObserver = object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ onAnimatorScaleChanged()
+ }
+ }
+
+ init {
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ /* notifyForDescendants= */ false,
+ animatorDurationScaleObserver)
+ onAnimatorScaleChanged()
+ }
+
+ private fun onAnimatorScaleChanged() {
+ val animationsEnabled = ValueAnimator.areAnimatorsEnabled()
+ scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(animationsEnabled)
+ }
+
+ override fun addCallback(listener: TransitionProgressListener) {
+ scopedUnfoldTransitionProgressProvider.addCallback(listener)
+ }
+
+ override fun removeCallback(listener: TransitionProgressListener) {
+ scopedUnfoldTransitionProgressProvider.removeCallback(listener)
+ }
+
+ override fun destroy() {
+ contentResolver.unregisterContentObserver(animatorDurationScaleObserver)
+ scopedUnfoldTransitionProgressProvider.destroy()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index c628d44..86b12d3c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -89,7 +89,6 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController;
- private int mLargeClockTopMargin = 0;
private int mKeyguardClockTopMargin = 0;
/**
@@ -276,16 +275,13 @@
}
private void updateClockLayout() {
- if (mSmartspaceController.isEnabled()) {
- RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
- MATCH_PARENT);
- mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize(
- R.dimen.keyguard_large_clock_top_margin);
- lp.topMargin = mLargeClockTopMargin;
- mLargeClockFrame.setLayoutParams(lp);
- } else {
- mLargeClockTopMargin = 0;
- }
+ int largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
+ R.dimen.keyguard_large_clock_top_margin);
+
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT);
+ lp.topMargin = largeClockTopMargin;
+ mLargeClockFrame.setLayoutParams(lp);
}
/**
@@ -358,8 +354,6 @@
mKeyguardUnlockAnimationController.updateLockscreenSmartSpacePosition();
}
}
-
- mKeyguardSliceViewController.updatePosition(x, props, animate);
}
/** Sets an alpha value on every child view except for the smartspace. */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 3ebd652..598325c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -320,6 +320,10 @@
mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
+ /**
+ * Fades and translates in/out the security screen.
+ * @param fraction amount of the screen that should show.
+ */
public void setExpansion(float fraction) {
float alpha = MathUtils.map(KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction);
mView.setAlpha(MathUtils.constrain(alpha, 0f, 1f));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index d05cc4e..2af9244 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -43,9 +43,6 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
@@ -203,13 +200,6 @@
Trace.endSection();
}
- /**
- * Update position of the view, with optional animation
- */
- void updatePosition(int x, AnimationProperties props, boolean animate) {
- PropertyAnimator.setProperty(mView, AnimatableProperty.TRANSLATION_X, x, props, animate);
- }
-
void showSlice(Slice slice) {
Trace.beginSection("KeyguardSliceViewController#showSlice");
if (slice == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e24f07c..2789e27 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -51,6 +51,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
+import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -144,7 +145,7 @@
private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_FINGERPRINT = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_SPEW = false;
- private static final int FINGERPRINT_LOCKOUT_RESET_DELAY_MS = 600;
+ private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
private static final String ACTION_FACE_UNLOCK_STARTED
= "com.android.facelock.FACE_UNLOCK_STARTED";
@@ -201,6 +202,19 @@
private static final int BIOMETRIC_STATE_CANCELLING = 2;
/**
+ * Action indicating keyguard *can* start biometric authentiation.
+ */
+ private static final int BIOMETRIC_ACTION_START = 0;
+ /**
+ * Action indicating keyguard *can* stop biometric authentiation.
+ */
+ private static final int BIOMETRIC_ACTION_STOP = 1;
+ /**
+ * Action indicating keyguard *can* start or stop biometric authentiation.
+ */
+ private static final int BIOMETRIC_ACTION_UPDATE = 2;
+
+ /**
* Biometric state: During cancelling we got another request to start listening, so when we
* receive the cancellation done signal, we should start listening again.
*/
@@ -322,6 +336,8 @@
private boolean mLockIconPressed;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final Executor mBackgroundExecutor;
+ private SensorPrivacyManager mSensorPrivacyManager;
+ private int mFaceAuthUserId;
/**
* Short delay before restarting fingerprint authentication after a successful try. This should
@@ -339,13 +355,13 @@
private final Runnable mFpCancelNotReceived = () -> {
Log.e(TAG, "Fp cancellation not received, transitioning to STOPPED");
mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
};
private final Runnable mFaceCancelNotReceived = () -> {
Log.e(TAG, "Face cancellation not received, transitioning to STOPPED");
mFaceRunningState = BIOMETRIC_STATE_STOPPED;
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_STOP);
};
private final Handler mHandler;
@@ -365,7 +381,7 @@
public void onChanged(boolean enabled, int userId) throws RemoteException {
mHandler.post(() -> {
mBiometricEnabledForUser.put(userId, enabled);
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
});
}
};
@@ -415,7 +431,6 @@
private final KeyguardListenQueue mListenModels = new KeyguardListenQueue();
private static int sCurrentUser;
- private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
public synchronized static void setCurrentUser(int currentUser) {
sCurrentUser = currentUser;
@@ -428,8 +443,17 @@
@Override
public void onTrustChanged(boolean enabled, int userId, int flags) {
Assert.isMainThread();
+ boolean wasTrusted = mUserHasTrust.get(userId, false);
mUserHasTrust.put(userId, enabled);
- updateBiometricListeningState();
+ // If there was no change in trusted state, make sure we are not authenticating.
+ // TrustManager sends an onTrustChanged whenever a user unlocks keyguard, for
+ // this reason we need to make sure to not authenticate.
+ if (wasTrusted == enabled) {
+ updateBiometricListeningState(BIOMETRIC_ACTION_STOP);
+ } else if (!enabled) {
+ updateBiometricListeningState(BIOMETRIC_ACTION_START);
+ }
+
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -594,7 +618,8 @@
*/
public void setCredentialAttempted() {
mCredentialAttempted = true;
- updateBiometricListeningState();
+ // Do not update face listening state in case of false authentication attempts.
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -602,7 +627,7 @@
*/
public void setKeyguardGoingAway(boolean goingAway) {
mKeyguardGoingAway = goingAway;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -610,7 +635,7 @@
*/
public void setKeyguardOccluded(boolean occluded) {
mKeyguardOccluded = occluded;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
@@ -622,7 +647,7 @@
*/
public void requestFaceAuthOnOccludingApp(boolean request) {
mOccludingAppRequestingFace = request;
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -633,7 +658,7 @@
*/
public void requestFingerprintAuthOnOccludingApp(boolean request) {
mOccludingAppRequestingFp = request;
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -641,7 +666,7 @@
*/
public void onCameraLaunched() {
mSecureCameraLaunched = true;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -676,7 +701,7 @@
}
// Don't send cancel if authentication succeeds
mFingerprintCancelSignal = null;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -772,7 +797,7 @@
Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
mHardwareFingerprintUnavailableRetryCount);
if (mFpm.isHardwareDetected()) {
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
mHardwareFingerprintUnavailableRetryCount++;
mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
@@ -792,7 +817,7 @@
if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
&& mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else {
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
@@ -801,17 +826,23 @@
mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
}
+ boolean lockedOutStateChanged = false;
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged |= !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
- requireStrongAuthIfAllLockedOut();
+ Log.d(TAG, "Fingerprint locked out - requiring strong auth");
+ mLockPatternUtils.requireStrongAuth(
+ STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, getCurrentUser());
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged |= !mFingerprintLockedOut;
mFingerprintLockedOut = true;
if (isUdfpsEnrolled()) {
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
+ stopListeningForFace();
}
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -820,9 +851,14 @@
cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
}
}
+
+ if (lockedOutStateChanged) {
+ notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ }
}
private void handleFingerprintLockoutReset() {
+ boolean changed = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
mFingerprintLockedOut = false;
mFingerprintLockedOutPermanent = false;
@@ -832,10 +868,15 @@
// that the events will arrive in a particular order. Add a delay here in case
// an unlock is in progress. In this is a normal unlock the extra delay won't
// be noticeable.
- mHandler.postDelayed(this::updateFingerprintListeningState,
- FINGERPRINT_LOCKOUT_RESET_DELAY_MS);
+ mHandler.postDelayed(() -> {
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ }, BIOMETRIC_LOCKOUT_RESET_DELAY_MS);
} else {
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ }
+
+ if (changed) {
+ notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
}
}
@@ -875,7 +916,7 @@
}
// Don't send cancel if authentication succeeds
mFaceCancelSignal = null;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -968,7 +1009,7 @@
public void run() {
Log.w(TAG, "Retrying face after HW unavailable, attempt " +
mHardwareFaceUnavailableRetryCount);
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
}
};
@@ -981,16 +1022,24 @@
// Error is always the end of authentication lifecycle
mFaceCancelSignal = null;
+ boolean cameraPrivacyEnabled = false;
+ if (mSensorPrivacyManager != null) {
+ cameraPrivacyEnabled = mSensorPrivacyManager
+ .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
+ mFaceAuthUserId);
+ }
if (msgId == FaceManager.FACE_ERROR_CANCELED
&& mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
} else {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
}
- if (msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE
+ final boolean isHwUnavailable = msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE;
+
+ if (isHwUnavailable
|| msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) {
if (mHardwareFaceUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
mHardwareFaceUnavailableRetryCount++;
@@ -999,9 +1048,14 @@
}
}
+ boolean lockedOutStateChanged = false;
if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged = !mFaceLockedOutPermanent;
mFaceLockedOutPermanent = true;
- requireStrongAuthIfAllLockedOut();
+ }
+
+ if (isHwUnavailable && cameraPrivacyEnabled) {
+ errString = mContext.getString(R.string.kg_face_sensor_privacy_enabled);
}
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1011,11 +1065,23 @@
BiometricSourceType.FACE);
}
}
+
+ if (lockedOutStateChanged) {
+ notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ }
}
private void handleFaceLockoutReset() {
+ boolean changed = mFaceLockedOutPermanent;
mFaceLockedOutPermanent = false;
- updateFaceListeningState();
+
+ mHandler.postDelayed(() -> {
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+ }, BIOMETRIC_LOCKOUT_RESET_DELAY_MS);
+
+ if (changed) {
+ notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ }
}
private void setFaceRunningState(int faceRunningState) {
@@ -1099,19 +1165,6 @@
return faceAuthenticated;
}
- private void requireStrongAuthIfAllLockedOut() {
- final boolean faceLock =
- (mFaceLockedOutPermanent || !shouldListenForFace()) && !getIsFaceAuthenticated();
- final boolean fpLock =
- mFingerprintLockedOutPermanent || !shouldListenForFingerprint(isUdfpsEnrolled());
-
- if (faceLock && fpLock) {
- Log.d(TAG, "All biometrics locked out - requiring strong auth");
- mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
- getCurrentUser());
- }
- }
-
public boolean getUserCanSkipBouncer(int userId) {
return getUserHasTrust(userId) || getUserUnlockedWithBiometric(userId);
}
@@ -1237,6 +1290,16 @@
}
}
+ private void notifyLockedOutStateChanged(BiometricSourceType type) {
+ Assert.isMainThread();
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onLockedOutStateChanged(type);
+ }
+ }
+ }
+
public boolean isScreenOn() {
return mScreenOn;
}
@@ -1254,7 +1317,7 @@
@VisibleForTesting
void setAssistantVisible(boolean assistantVisible) {
mAssistantVisible = assistantVisible;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
static class DisplayClientState {
@@ -1593,7 +1656,7 @@
protected void handleStartedWakingUp() {
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
Assert.isMainThread();
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1614,7 +1677,7 @@
}
}
mGoingToSleep = true;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
protected void handleFinishedGoingToSleep(int arg1) {
@@ -1626,7 +1689,7 @@
cb.onFinishedGoingToSleep(arg1);
}
}
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
private void handleScreenTurnedOn() {
@@ -1663,7 +1726,7 @@
cb.onDreamingStateChanged(mIsDreaming);
}
}
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
private void handleUserInfoChanged(int userId) {
@@ -1757,6 +1820,7 @@
mLockPatternUtils = lockPatternUtils;
mAuthController = authController;
dumpManager.registerDumpable(getClass().getName(), this);
+ mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
mHandler = new Handler(mainLooper) {
@Override
@@ -1854,7 +1918,7 @@
setAssistantVisible((boolean) msg.obj);
break;
case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
break;
case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
updateLogoutEnabled();
@@ -1972,10 +2036,10 @@
@Override
public void onEnrollmentsChanged() {
- mainExecutor.execute(() -> updateBiometricListeningState());
+ mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE));
}
});
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
if (mFpm != null) {
mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
}
@@ -2089,12 +2153,12 @@
mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
}
- private void updateBiometricListeningState() {
- updateFingerprintListeningState();
- updateFaceListeningState();
+ private void updateBiometricListeningState(int action) {
+ updateFingerprintListeningState(action);
+ updateFaceListeningState(action);
}
- private void updateFingerprintListeningState() {
+ private void updateFingerprintListeningState(int action) {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
@@ -2106,8 +2170,16 @@
final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
if (runningOrRestarting && !shouldListenForFingerprint) {
+ if (action == BIOMETRIC_ACTION_START) {
+ Log.v(TAG, "Ignoring stopListeningForFingerprint()");
+ return;
+ }
stopListeningForFingerprint();
} else if (!runningOrRestarting && shouldListenForFingerprint) {
+ if (action == BIOMETRIC_ACTION_STOP) {
+ Log.v(TAG, "Ignoring startListeningForFingerprint()");
+ return;
+ }
startListeningForFingerprint();
}
}
@@ -2136,7 +2208,7 @@
return;
}
mAuthInterruptActive = active;
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -2147,7 +2219,7 @@
public void requestFaceAuth(boolean userInitiatedRequest) {
if (DEBUG) Log.d(TAG, "requestFaceAuth() userInitiated=" + userInitiatedRequest);
mIsFaceAuthUserRequested |= userInitiatedRequest;
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
}
public boolean isFaceAuthUserRequested() {
@@ -2161,7 +2233,7 @@
stopListeningForFace();
}
- private void updateFaceListeningState() {
+ private void updateFaceListeningState(int action) {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
@@ -2170,9 +2242,17 @@
mHandler.removeCallbacks(mRetryFaceAuthentication);
boolean shouldListenForFace = shouldListenForFace();
if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) {
+ if (action == BIOMETRIC_ACTION_START) {
+ Log.v(TAG, "Ignoring stopListeningForFace()");
+ return;
+ }
mIsFaceAuthUserRequested = false;
stopListeningForFace();
} else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING && shouldListenForFace) {
+ if (action == BIOMETRIC_ACTION_STOP) {
+ Log.v(TAG, "Ignoring startListeningForFace()");
+ return;
+ }
startListeningForFace();
}
}
@@ -2282,6 +2362,9 @@
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+ // TODO: always disallow when fp is already locked out?
+ final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
// There's no reason to ask the HAL for authentication when the user can dismiss the
@@ -2316,7 +2399,8 @@
&& !mKeyguardGoingAway && biometricEnabledForUser && !mLockIconPressed
&& strongAuthAllowsScanning && mIsPrimaryUser
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
- && !faceAuthenticated;
+ && !faceAuthenticated
+ && !fpLockedout;
// Aggregate relevant fields for debug logging.
if (DEBUG_FACE || DEBUG_SPEW) {
@@ -2380,7 +2464,7 @@
mLockIconPressed = true;
final int userId = getCurrentUser();
mUserFaceAuthenticated.put(userId, null);
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
mStrongAuthTracker.onStrongAuthRequiredChanged(userId);
}
@@ -2442,6 +2526,7 @@
// This would need to be updated for multi-sensor devices
final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
&& mFaceSensorProperties.get(0).supportsFaceDetection;
+ mFaceAuthUserId = userId;
if (isEncryptedOrLockdown(userId) && supportsFaceDetection) {
mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
} else {
@@ -2454,6 +2539,10 @@
}
}
+ public boolean isFingerprintLockedOut() {
+ return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+ }
+
/**
* If biometrics hardware is available, not disabled, and user has enrolled templates.
* This does NOT check if the device is encrypted or in lockdown.
@@ -2552,7 +2641,7 @@
*/
private void handleDevicePolicyManagerStateChanged(int userId) {
Assert.isMainThread();
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
updateSecondaryLockscreenRequirement(userId);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2842,7 +2931,7 @@
cb.onKeyguardVisibilityChangedRaw(showing);
}
}
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
/** Notifies that the occluded state changed. */
@@ -2864,7 +2953,7 @@
*/
private void handleKeyguardReset() {
if (DEBUG) Log.d(TAG, "handleKeyguardReset");
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
}
@@ -2910,7 +2999,7 @@
cb.onKeyguardBouncerChanged(mBouncer);
}
}
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -3030,7 +3119,9 @@
public void setSwitchingUser(boolean switching) {
mSwitchingUser = switching;
// Since this comes in on a binder thread, we need to post if first
- mHandler.post(mUpdateBiometricListeningState);
+ mHandler.post(() -> {
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ });
}
private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 12431984..8170a81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -292,6 +292,11 @@
public void onStrongAuthStateChanged(int userId) { }
/**
+ * When the current user's locked out state changed.
+ */
+ public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) { }
+
+ /**
* Called when the dream's window state is changed.
* @param dreaming true if the dream's window has been created and is visible
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 68132f4..0c1934c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
@@ -30,6 +31,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -82,14 +84,18 @@
void updateColorAndBackgroundVisibility() {
if (mUseBackground && mLockIcon.getDrawable() != null) {
- mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.textColorPrimary);
+ mLockIconColor = ColorUtils.blendARGB(
+ Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
+ Color.WHITE,
+ mDozeAmount);
mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
mBgView.setAlpha(1f - mDozeAmount);
mBgView.setVisibility(View.VISIBLE);
} else {
- mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
- R.attr.wallpaperTextColorAccent);
+ mLockIconColor = ColorUtils.blendARGB(
+ Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
+ Color.WHITE,
+ mDozeAmount);
mBgView.setVisibility(View.GONE);
}
@@ -212,11 +218,14 @@
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("Center in px (x, y)= (" + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
- pw.println("Radius in pixels: " + mRadius);
- pw.println("topLeft= (" + getX() + ", " + getY() + ")");
- pw.println("topLeft= (" + getX() + ", " + getY() + ")");
- pw.println("mIconType=" + typeToString(mIconType));
- pw.println("mAod=" + mAod);
+ pw.println("Lock Icon View Parameters:");
+ pw.println(" Center in px (x, y)= ("
+ + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
+ pw.println(" Radius in pixels: " + mRadius);
+ pw.println(" mIconType=" + typeToString(mIconType));
+ pw.println(" mAod=" + mAod);
+ pw.println("Lock Icon View actual measurements:");
+ pw.println(" topLeft= (" + getX() + ", " + getY() + ")");
+ pw.println(" width=" + getWidth() + " height=" + getHeight());
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8847639..cc452c6 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -37,11 +37,10 @@
import android.os.Process;
import android.os.Vibrator;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.MathUtils;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -67,8 +66,6 @@
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.airbnb.lottie.LottieAnimationView;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
@@ -83,6 +80,7 @@
*/
@StatusBarComponent.StatusBarScope
public class LockIconViewController extends ViewController<LockIconView> implements Dumpable {
+ private static final String TAG = "LockIconViewController";
private static final float sDefaultDensity =
(float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36);
@@ -91,6 +89,7 @@
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
+ private static final long LONG_PRESS_TIMEOUT = 200L; // milliseconds
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final KeyguardViewController mKeyguardViewController;
@@ -101,10 +100,8 @@
@NonNull private final AccessibilityManager mAccessibilityManager;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final DelayableExecutor mExecutor;
- @NonNull private final LayoutInflater mLayoutInflater;
private boolean mUdfpsEnrolled;
- @Nullable private LottieAnimationView mAodFp;
@NonNull private final AnimatedStateListDrawable mIcon;
@NonNull private CharSequence mUnlockedLabel;
@@ -112,6 +109,11 @@
@Nullable private final Vibrator mVibrator;
@Nullable private final AuthRippleController mAuthRippleController;
+ // Tracks the velocity of a touch to help filter out the touches that move too fast.
+ private VelocityTracker mVelocityTracker;
+ // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
+ private int mActivePointerId = -1;
+
private boolean mIsDozing;
private boolean mIsBouncerShowing;
private boolean mRunningFPS;
@@ -122,6 +124,7 @@
private boolean mUserUnlockedWithBiometric;
private Runnable mCancelDelayedUpdateVisibilityRunnable;
private Runnable mOnGestureDetectedRunnable;
+ private Runnable mLongPressCancelRunnable;
private boolean mUdfpsSupported;
private float mHeightPixels;
@@ -133,7 +136,7 @@
// for udfps when strong auth is required or unlocked on AOD
private boolean mShowAodLockIcon;
- private boolean mShowAODFpIcon;
+ private boolean mShowAodUnlockedIcon;
private final int mMaxBurnInOffsetX;
private final int mMaxBurnInOffsetY;
private float mInterpolatedDarkAmount;
@@ -156,8 +159,7 @@
@NonNull @Main DelayableExecutor executor,
@Nullable Vibrator vibrator,
@Nullable AuthRippleController authRippleController,
- @NonNull @Main Resources resources,
- @NonNull LayoutInflater inflater
+ @NonNull @Main Resources resources
) {
super(view);
mStatusBarStateController = statusBarStateController;
@@ -171,7 +173,6 @@
mExecutor = executor;
mVibrator = vibrator;
mAuthRippleController = authRippleController;
- mLayoutInflater = inflater;
mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
@@ -181,7 +182,7 @@
mView.setImageDrawable(mIcon);
mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
- dumpManager.registerDumpable("LockIconViewController", this);
+ dumpManager.registerDumpable(TAG, this);
}
@Override
@@ -253,11 +254,12 @@
}
boolean wasShowingUnlock = mShowUnlockIcon;
- boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon;
+ boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
+ && !mShowAodUnlockedIcon && !mShowAodLockIcon;
mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
&& (!mUdfpsEnrolled || !mRunningFPS);
mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
- mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
+ mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
final CharSequence prevContentDescription = mView.getContentDescription();
@@ -274,20 +276,11 @@
mView.updateIcon(ICON_UNLOCK, false);
mView.setContentDescription(mUnlockedLabel);
mView.setVisibility(View.VISIBLE);
- } else if (mShowAODFpIcon) {
- // AOD fp icon is special cased as a lottie view (it updates for each burn-in offset),
- // this state shows a transparent view
- mView.setContentDescription(null);
- mAodFp.setVisibility(View.VISIBLE);
- mAodFp.setContentDescription(mCanDismissLockScreen ? mUnlockedLabel : mLockedLabel);
-
- mView.updateIcon(ICON_FINGERPRINT, true); // this shows no icon
+ } else if (mShowAodUnlockedIcon) {
+ mView.updateIcon(ICON_UNLOCK, true);
+ mView.setContentDescription(mUnlockedLabel);
mView.setVisibility(View.VISIBLE);
} else if (mShowAodLockIcon) {
- if (wasShowingUnlock) {
- // transition to the unlock icon first
- mView.updateIcon(ICON_LOCK, false);
- }
mView.updateIcon(ICON_LOCK, true);
mView.setContentDescription(mLockedLabel);
mView.setVisibility(View.VISIBLE);
@@ -297,11 +290,6 @@
mView.setContentDescription(null);
}
- if (!mShowAODFpIcon && mAodFp != null) {
- mAodFp.setVisibility(View.INVISIBLE);
- mAodFp.setContentDescription(null);
- }
-
if (!Objects.equals(prevContentDescription, mView.getContentDescription())
&& mView.getContentDescription() != null) {
mView.announceForAccessibility(mView.getContentDescription());
@@ -320,7 +308,7 @@
getResources().getString(R.string.accessibility_enter_hint));
public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(v, info);
- if (isClickable()) {
+ if (isActionable()) {
if (mShowLockIcon) {
info.addAction(mAccessibilityAuthenticateHint);
} else if (mShowUnlockIcon) {
@@ -389,7 +377,7 @@
pw.println();
pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
pw.println(" mShowLockIcon: " + mShowLockIcon);
- pw.println(" mShowAODFpIcon: " + mShowAODFpIcon);
+ pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
pw.println(" mIsDozing: " + mIsDozing);
pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
@@ -418,17 +406,8 @@
- mMaxBurnInOffsetY, mInterpolatedDarkAmount);
float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount);
- if (mAodFp != null) {
- mAodFp.setTranslationX(offsetX);
- mAodFp.setTranslationY(offsetY);
- mAodFp.setProgress(progress);
- mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
- }
-
- if (mShowAodLockIcon) {
- mView.setTranslationX(offsetX);
- mView.setTranslationY(offsetY);
- }
+ mView.setTranslationX(offsetX);
+ mView.setTranslationY(offsetY);
}
private void updateIsUdfpsEnrolled() {
@@ -439,10 +418,6 @@
mView.setUseBackground(mUdfpsSupported);
mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
- if (!wasUdfpsEnrolled && mUdfpsEnrolled && mAodFp == null) {
- mLayoutInflater.inflate(R.layout.udfps_aod_lock_icon, mView);
- mAodFp = mView.findViewById(R.id.lock_udfps_aod_fp);
- }
if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
updateVisibility();
}
@@ -477,7 +452,7 @@
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
// reset mIsBouncerShowing state in case it was preemptively set
- // onAffordanceClick
+ // onLongPress
mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
updateVisibility();
}
@@ -571,117 +546,145 @@
}
};
- private final GestureDetector mGestureDetector =
- new GestureDetector(new SimpleOnGestureListener() {
- public boolean onDown(MotionEvent e) {
- if (!isClickable()) {
- mDownDetected = false;
- return false;
- }
-
- // intercept all following touches until we see MotionEvent.ACTION_CANCEL UP or
- // MotionEvent.ACTION_UP (see #onTouchEvent)
- if (mVibrator != null && !mDownDetected) {
- mVibrator.vibrate(
- Process.myUid(),
- getContext().getOpPackageName(),
- UdfpsController.EFFECT_CLICK,
- "lockIcon-onDown",
- VIBRATION_SONIFICATION_ATTRIBUTES);
- }
-
- mDownDetected = true;
- return true;
- }
-
- public void onLongPress(MotionEvent e) {
- if (!wasClickableOnDownEvent()) {
- return;
- }
-
- if (onAffordanceClick() && mVibrator != null) {
- // only vibrate if the click went through and wasn't intercepted by falsing
- mVibrator.vibrate(
- Process.myUid(),
- getContext().getOpPackageName(),
- UdfpsController.EFFECT_CLICK,
- "lockIcon-onLongPress",
- VIBRATION_SONIFICATION_ATTRIBUTES);
- }
- }
-
- public boolean onSingleTapUp(MotionEvent e) {
- if (!wasClickableOnDownEvent()) {
- return false;
- }
- onAffordanceClick();
- return true;
- }
-
- public boolean onFling(MotionEvent e1, MotionEvent e2,
- float velocityX, float velocityY) {
- if (!wasClickableOnDownEvent()) {
- return false;
- }
- onAffordanceClick();
- return true;
- }
-
- private boolean wasClickableOnDownEvent() {
- return mDownDetected;
- }
-
- /**
- * Whether we tried to launch the affordance.
- *
- * If falsing intercepts the click, returns false.
- */
- private boolean onAffordanceClick() {
- if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
- return false;
- }
-
- // pre-emptively set to true to hide view
- mIsBouncerShowing = true;
- if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
- mAuthRippleController.showRipple(FINGERPRINT);
- }
- updateVisibility();
- if (mOnGestureDetectedRunnable != null) {
- mOnGestureDetectedRunnable.run();
- }
- mKeyguardViewController.showBouncer(/* scrim */ true);
- return true;
- }
- });
-
/**
- * Send touch events to this view and handles it if the touch is within this view and we are
- * in a 'clickable' state
- * @return whether to intercept the touch event
+ * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true.
+ * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
+ * area for {@link #LONG_PRESS_TIMEOUT} ms.
+ *
+ * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
*/
public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) {
- if (mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
- && (mView.getVisibility() == View.VISIBLE
- || (mAodFp != null && mAodFp.getVisibility() == View.VISIBLE))) {
- mOnGestureDetectedRunnable = onGestureDetectedRunnable;
- mGestureDetector.onTouchEvent(event);
+ if (!onInterceptTouchEvent(event)) {
+ cancelTouches();
+ return false;
}
- // we continue to intercept all following touches until we see MotionEvent.ACTION_CANCEL UP
- // or MotionEvent.ACTION_UP. this is to avoid passing the touch to NPV
- // after the lock icon disappears on device entry
- if (mDownDetected) {
- if (event.getAction() == MotionEvent.ACTION_CANCEL
- || event.getAction() == MotionEvent.ACTION_UP) {
- mDownDetected = false;
- }
- return true;
+ mOnGestureDetectedRunnable = onGestureDetectedRunnable;
+ switch(event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_HOVER_ENTER:
+ if (mVibrator != null && !mDownDetected) {
+ mVibrator.vibrate(
+ Process.myUid(),
+ getContext().getOpPackageName(),
+ UdfpsController.EFFECT_CLICK,
+ "lock-icon-down",
+ VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+
+ // The pointer that causes ACTION_DOWN is always at index 0.
+ // We need to persist its ID to track it during ACTION_MOVE that could include
+ // data for many other pointers because of multi-touch support.
+ mActivePointerId = event.getPointerId(0);
+ if (mVelocityTracker == null) {
+ // To simplify the lifecycle of the velocity tracker, make sure it's never null
+ // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
+ mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
+ // ACTION_DOWN, in that case we should just reuse the old instance.
+ mVelocityTracker.clear();
+ }
+ mVelocityTracker.addMovement(event);
+
+ mDownDetected = true;
+ mLongPressCancelRunnable = mExecutor.executeDelayed(
+ this::onLongPress, LONG_PRESS_TIMEOUT);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ mVelocityTracker.addMovement(event);
+ // Compute pointer velocity in pixels per second.
+ mVelocityTracker.computeCurrentVelocity(1000);
+ float velocity = UdfpsController.computePointerSpeed(mVelocityTracker,
+ mActivePointerId);
+ if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS
+ && UdfpsController.exceedsVelocityThreshold(velocity)) {
+ Log.v(TAG, "lock icon long-press rescheduled due to "
+ + "high pointer velocity=" + velocity);
+ mLongPressCancelRunnable.run();
+ mLongPressCancelRunnable = mExecutor.executeDelayed(
+ this::onLongPress, LONG_PRESS_TIMEOUT);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_HOVER_EXIT:
+ cancelTouches();
+ break;
}
- return false;
+
+ return true;
}
- private boolean isClickable() {
+ /**
+ * Intercepts the touch if the onDown event and current event are within this lock icon view's
+ * bounds.
+ */
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (!inLockIconArea(event) || !isActionable()) {
+ return false;
+ }
+
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ return true;
+ }
+
+ return mDownDetected;
+ }
+
+ private void onLongPress() {
+ cancelTouches();
+ if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+ Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
+ return;
+ }
+
+ // pre-emptively set to true to hide view
+ mIsBouncerShowing = true;
+ if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
+ mAuthRippleController.showRipple(FINGERPRINT);
+ }
+ updateVisibility();
+ if (mOnGestureDetectedRunnable != null) {
+ mOnGestureDetectedRunnable.run();
+ }
+
+ if (mVibrator != null) {
+ // play device entry haptic (same as biometric success haptic)
+ mVibrator.vibrate(
+ Process.myUid(),
+ getContext().getOpPackageName(),
+ UdfpsController.EFFECT_CLICK,
+ "lock-icon-device-entry",
+ VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+
+ mKeyguardViewController.showBouncer(/* scrim */ true);
+ }
+
+
+ private void cancelTouches() {
+ mDownDetected = false;
+ if (mLongPressCancelRunnable != null) {
+ mLongPressCancelRunnable.run();
+ }
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ if (mVibrator != null) {
+ mVibrator.cancel();
+ }
+ }
+
+
+ private boolean inLockIconArea(MotionEvent event) {
+ return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
+ && mView.getVisibility() == View.VISIBLE;
+ }
+
+ private boolean isActionable() {
return mUdfpsSupported || mShowUnlockIcon;
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index d37dcaf..db88569 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -121,7 +121,8 @@
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
- .setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI()));
+ .setCompatUI(Optional.of(mWMComponent.getCompatUI()))
+ .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
// is separating this logic into newly creating SystemUITestsFactory.
@@ -140,7 +141,8 @@
.setStartingSurface(Optional.ofNullable(null))
.setTaskSurfaceHelper(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
- .setSizeCompatUI(Optional.ofNullable(null));
+ .setCompatUI(Optional.ofNullable(null))
+ .setDragAndDrop(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (mInitializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
index c941d66..e4e0da6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
@@ -71,7 +71,9 @@
public void addListener(@NonNull T listener) {
Objects.requireNonNull(listener, "listener must be non-null");
- mListeners.add(listener);
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
if (mListeners.size() == 1) {
mContentResolver.registerContentObserver(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index cff6cf1..cc5a792 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -98,7 +98,8 @@
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- init();
+ mIsKeyguardVisible = false;
+ mIsAccessibilityManagerServiceReady = false;
}
/**
@@ -124,9 +125,8 @@
handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
}
- private void init() {
- mIsKeyguardVisible = false;
- mIsAccessibilityManagerServiceReady = false;
+ /** Initializes the AccessibilityFloatingMenuController configurations. */
+ public void init() {
mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
mBtnTargets = mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
registerContentObservers();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index f11dc93..1496f17 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -636,6 +636,9 @@
mIndicatorView.setText(message);
mIndicatorView.setTextColor(mTextColorError);
mIndicatorView.setVisibility(View.VISIBLE);
+ // select to enable marquee unless a screen reader is enabled
+ mIndicatorView.setSelected(!mAccessibilityManager.isEnabled()
+ || !mAccessibilityManager.isTouchExplorationEnabled());
mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 1226dca..df20b83 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -31,6 +31,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PointF;
+import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
@@ -48,7 +49,6 @@
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -64,6 +64,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.Execution;
import java.util.ArrayList;
import java.util.Arrays;
@@ -89,16 +90,22 @@
private static final String TAG = "AuthController";
private static final boolean DEBUG = true;
+ private static final int SENSOR_PRIVACY_DELAY = 500;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Handler mHandler;
+ private final Execution mExecution;
private final CommandQueue mCommandQueue;
private final ActivityTaskManager mActivityTaskManager;
- @Nullable private final FingerprintManager mFingerprintManager;
- @Nullable private final FaceManager mFaceManager;
+ @Nullable
+ private final FingerprintManager mFingerprintManager;
+ @Nullable
+ private final FaceManager mFaceManager;
private final Provider<UdfpsController> mUdfpsControllerFactory;
private final Provider<SidefpsController> mSidefpsControllerFactory;
- @Nullable private final PointF mFaceAuthSensorLocation;
- @Nullable private PointF mFingerprintLocation;
+ @Nullable
+ private final PointF mFaceAuthSensorLocation;
+ @Nullable
+ private PointF mFingerprintLocation;
private final Set<Callback> mCallbacks = new HashSet<>();
// TODO: These should just be saved from onSaveState
@@ -122,6 +129,7 @@
@Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
@NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
+ private SensorPrivacyManager mSensorPrivacyManager;
private class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -130,62 +138,27 @@
}
}
- private final FingerprintStateListener mFingerprintStateListener =
- new FingerprintStateListener() {
- @Override
- public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
- Log.d(TAG, "onEnrollmentsChanged, userId: " + userId
- + ", sensorId: " + sensorId
- + ", hasEnrollments: " + hasEnrollments);
- for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
- if (prop.sensorId == sensorId) {
- mUdfpsEnrolledForUser.put(userId, hasEnrollments);
- }
- }
-
- for (Callback cb : mCallbacks) {
- cb.onEnrollmentsChanged();
- }
- }
- };
-
- @NonNull
private final IFingerprintAuthenticatorsRegisteredCallback
mFingerprintAuthenticatorsRegisteredCallback =
new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
- @Override public void onAllAuthenticatorsRegistered(
+ @Override
+ public void onAllAuthenticatorsRegistered(
List<FingerprintSensorPropertiesInternal> sensors) {
- if (DEBUG) {
- Log.d(TAG, "onFingerprintProvidersAvailable | sensors: " + Arrays.toString(
- sensors.toArray()));
- }
- mFpProps = sensors;
- List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
- List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
- for (FingerprintSensorPropertiesInternal props : mFpProps) {
- if (props.isAnyUdfpsType()) {
- udfpsProps.add(props);
- }
- if (props.isAnySidefpsType()) {
- sidefpsProps.add(props);
- }
- }
- mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
- if (mUdfpsProps != null) {
- mUdfpsController = mUdfpsControllerFactory.get();
- }
- mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
- if (mSidefpsProps != null) {
- mSidefpsController = mSidefpsControllerFactory.get();
- }
-
- for (Callback cb : mCallbacks) {
- cb.onAllAuthenticatorsRegistered();
- }
+ mHandler.post(() -> handleAllAuthenticatorsRegistered(sensors));
}
};
- @VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private final FingerprintStateListener mFingerprintStateListener =
+ new FingerprintStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mHandler.post(
+ () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments));
+ }
+ };
+
+ @VisibleForTesting
+ final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mCurrentDialog != null
@@ -209,6 +182,7 @@
};
private void handleTaskStackChanged() {
+ mExecution.assertIsMainThread();
if (mCurrentDialog != null) {
try {
final String clientPackage = mCurrentDialog.getOpPackageName();
@@ -238,6 +212,56 @@
}
}
+ private void handleAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ mExecution.assertIsMainThread();
+ if (DEBUG) {
+ Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString(
+ sensors.toArray()));
+ }
+ mFpProps = sensors;
+ List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
+ List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
+ for (FingerprintSensorPropertiesInternal props : mFpProps) {
+ if (props.isAnyUdfpsType()) {
+ udfpsProps.add(props);
+ }
+ if (props.isAnySidefpsType()) {
+ sidefpsProps.add(props);
+ }
+ }
+ mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
+ if (mUdfpsProps != null) {
+ mUdfpsController = mUdfpsControllerFactory.get();
+ }
+ mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
+ if (mSidefpsProps != null) {
+ mSidefpsController = mSidefpsControllerFactory.get();
+ }
+ for (Callback cb : mCallbacks) {
+ cb.onAllAuthenticatorsRegistered();
+ }
+ mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
+ }
+
+ private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mExecution.assertIsMainThread();
+ Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId
+ + ", hasEnrollments: " + hasEnrollments);
+ if (mUdfpsProps == null) {
+ Log.d(TAG, "handleEnrollmentsChanged, mUdfpsProps is null");
+ } else {
+ for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
+ if (prop.sensorId == sensorId) {
+ mUdfpsEnrolledForUser.put(userId, hasEnrollments);
+ }
+ }
+ }
+ for (Callback cb : mCallbacks) {
+ cb.onEnrollmentsChanged();
+ }
+ }
+
/**
* Adds a callback. See {@link Callback}.
*/
@@ -446,6 +470,7 @@
@Inject
public AuthController(Context context,
+ Execution execution,
CommandQueue commandQueue,
ActivityTaskManager activityTaskManager,
@NonNull WindowManager windowManager,
@@ -456,6 +481,8 @@
@NonNull DisplayManager displayManager,
@Main Handler handler) {
super(context);
+ mExecution = execution;
+ mHandler = handler;
mCommandQueue = commandQueue;
mActivityTaskManager = activityTaskManager;
mFingerprintManager = fingerprintManager;
@@ -467,7 +494,7 @@
mOrientationListener = new BiometricDisplayListener(
context,
displayManager,
- handler,
+ mHandler,
BiometricDisplayListener.SensorType.Generic.INSTANCE,
() -> {
onOrientationChanged();
@@ -492,6 +519,7 @@
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.registerReceiver(mBroadcastReceiver, filter);
+ mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
}
private void updateFingerprintLocation() {
@@ -517,7 +545,6 @@
if (mFingerprintManager != null) {
mFingerprintManager.addAuthenticatorsRegisteredCallback(
mFingerprintAuthenticatorsRegisteredCallback);
- mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
}
mTaskStackListener = new BiometricTaskStackListener();
@@ -642,10 +669,16 @@
final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
|| (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
+ boolean isCameraPrivacyEnabled = false;
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE
+ && mSensorPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
+ mCurrentDialogArgs.argi1 /* userId */)) {
+ isCameraPrivacyEnabled = true;
+ }
// TODO(b/141025588): Create separate methods for handling hard and soft errors.
final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
- || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT);
-
+ || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT
+ || isCameraPrivacyEnabled);
if (mCurrentDialog != null) {
if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
@@ -655,12 +688,23 @@
? mContext.getString(R.string.biometric_not_recognized)
: getErrorString(modality, error, vendorCode);
if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
- mCurrentDialog.onAuthenticationFailed(modality, errorMessage);
+ // The camera privacy error can return before the prompt initializes its state,
+ // causing the prompt to appear to endlessly authenticate. Add a small delay
+ // to stop this.
+ if (isCameraPrivacyEnabled) {
+ mHandler.postDelayed(() -> {
+ mCurrentDialog.onAuthenticationFailed(modality,
+ mContext.getString(R.string.face_sensor_privacy_enabled));
+ }, SENSOR_PRIVACY_DELAY);
+ } else {
+ mCurrentDialog.onAuthenticationFailed(modality, errorMessage);
+ }
} else {
final String errorMessage = getErrorString(modality, error, vendorCode);
if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
mCurrentDialog.onError(modality, errorMessage);
}
+
} else {
Log.w(TAG, "onBiometricError callback but dialog is gone");
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 90a1e5e..f82ea79 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -80,11 +80,6 @@
private var circleReveal: LightRevealEffect? = null
private var udfpsController: UdfpsController? = null
-
- private var dwellScale = 2f
- private var expandedDwellScale = 2.5f
- private var aodDwellScale = 1.9f
- private var aodExpandedDwellScale = 2.3f
private var udfpsRadius: Float = -1f
override fun onInit() {
@@ -128,7 +123,7 @@
updateSensorLocation()
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
fingerprintSensorLocation != null) {
- mView.setSensorLocation(fingerprintSensorLocation!!)
+ mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius)
showUnlockedRipple()
} else if (biometricSourceType == BiometricSourceType.FACE &&
faceSensorLocation != null) {
@@ -241,24 +236,12 @@
}
private fun updateRippleColor() {
- mView.setColor(
- Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
+ mView.setLockScreenColor(Utils.getColorAttrDefaultColor(sysuiContext,
+ R.attr.wallpaperTextColorAccent))
}
private fun showDwellRipple() {
- if (statusBarStateController.isDozing) {
- mView.startDwellRipple(
- /* startRadius */ udfpsRadius,
- /* endRadius */ udfpsRadius * aodDwellScale,
- /* expandedRadius */ udfpsRadius * aodExpandedDwellScale,
- /* isDozing */ true)
- } else {
- mView.startDwellRipple(
- /* startRadius */ udfpsRadius,
- /* endRadius */ udfpsRadius * dwellScale,
- /* expandedRadius */ udfpsRadius * expandedDwellScale,
- /* isDozing */ false)
- }
+ mView.startDwellRipple(statusBarStateController.isDozing)
}
private val keyguardUpdateMonitorCallback =
@@ -295,7 +278,7 @@
return
}
- mView.setSensorLocation(fingerprintSensorLocation!!)
+ mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius)
showDwellRipple()
}
@@ -307,8 +290,8 @@
private val authControllerCallback =
object : AuthController.Callback {
override fun onAllAuthenticatorsRegistered() {
- updateSensorLocation()
updateUdfpsDependentParams()
+ updateSensorLocation()
}
override fun onEnrollmentsChanged() {
@@ -329,20 +312,6 @@
}
inner class AuthRippleCommand : Command {
- fun printLockScreenDwellInfo(pw: PrintWriter) {
- pw.println("lock screen dwell ripple: " +
- "\n\tsensorLocation=$fingerprintSensorLocation" +
- "\n\tdwellScale=$dwellScale" +
- "\n\tdwellExpand=$expandedDwellScale")
- }
-
- fun printAodDwellInfo(pw: PrintWriter) {
- pw.println("aod dwell ripple: " +
- "\n\tsensorLocation=$fingerprintSensorLocation" +
- "\n\tdwellScale=$aodDwellScale" +
- "\n\tdwellExpand=$aodExpandedDwellScale")
- }
-
override fun execute(pw: PrintWriter, args: List<String>) {
if (args.isEmpty()) {
invalidCommand(pw)
@@ -350,11 +319,9 @@
when (args[0]) {
"dwell" -> {
showDwellRipple()
- if (statusBarStateController.isDozing) {
- printAodDwellInfo(pw)
- } else {
- printLockScreenDwellInfo(pw)
- }
+ pw.println("lock screen dwell ripple: " +
+ "\n\tsensorLocation=$fingerprintSensorLocation" +
+ "\n\tudfpsRadius=$udfpsRadius")
}
"fingerprint" -> {
updateSensorLocation()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index c6d26ff..d673630 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -21,6 +21,7 @@
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
+import android.graphics.Color
import android.graphics.Paint
import android.graphics.PointF
import android.util.AttributeSet
@@ -28,6 +29,7 @@
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.animation.Interpolators
+import com.android.systemui.statusbar.charging.DwellRippleShader
import com.android.systemui.statusbar.charging.RippleShader
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
@@ -43,23 +45,32 @@
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f)
- private val dwellPulseDuration = 50L
- private val dwellAlphaDuration = dwellPulseDuration
- private val dwellAlpha: Float = 1f
- private val dwellExpandDuration = 1200L - dwellPulseDuration
+ private val dwellPulseDuration = 100L
+ private val dwellExpandDuration = 2000L - dwellPulseDuration
- private val aodDwellPulseDuration = 50L
- private var aodDwellAlphaDuration = aodDwellPulseDuration
- private var aodDwellAlpha: Float = .8f
- private var aodDwellExpandDuration = 1200L - aodDwellPulseDuration
+ private var drawDwell: Boolean = false
+ private var drawRipple: Boolean = false
+ private var lockScreenColorVal = Color.WHITE
private val retractDuration = 400L
private var alphaInDuration: Long = 0
private var unlockedRippleInProgress: Boolean = false
+ private val dwellShader = DwellRippleShader()
+ private val dwellPaint = Paint()
private val rippleShader = RippleShader()
private val ripplePaint = Paint()
private var retractAnimator: Animator? = null
private var dwellPulseOutAnimator: Animator? = null
+ private var dwellRadius: Float = 0f
+ set(value) {
+ dwellShader.maxRadius = value
+ field = value
+ }
+ private var dwellOrigin: PointF = PointF()
+ set(value) {
+ dwellShader.origin = value
+ field = value
+ }
private var radius: Float = 0f
set(value) {
rippleShader.radius = value
@@ -76,6 +87,11 @@
rippleShader.progress = 0f
rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
ripplePaint.shader = rippleShader
+
+ dwellShader.color = 0xffffffff.toInt() // default color
+ dwellShader.progress = 0f
+ dwellShader.distortionStrength = .4f
+ dwellPaint.shader = dwellShader
visibility = GONE
}
@@ -84,6 +100,13 @@
radius = maxOf(location.x, location.y, width - location.x, height - location.y).toFloat()
}
+ fun setFingerprintSensorLocation(location: PointF, sensorRadius: Float) {
+ origin = location
+ radius = maxOf(location.x, location.y, width - location.x, height - location.y).toFloat()
+ dwellOrigin = location
+ dwellRadius = sensorRadius * 1.5f
+ }
+
fun setAlphaInDuration(duration: Long) {
alphaInDuration = duration
}
@@ -97,14 +120,14 @@
}
if (dwellPulseOutAnimator?.isRunning == true) {
- val retractRippleAnimator = ValueAnimator.ofFloat(rippleShader.progress, 0f)
+ val retractRippleAnimator = ValueAnimator.ofFloat(dwellShader.progress, 0f)
.apply {
interpolator = retractInterpolator
duration = retractDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
+ dwellShader.progress = animator.animatedValue as Float
+ dwellShader.time = now.toFloat()
invalidate()
}
@@ -114,8 +137,8 @@
interpolator = Interpolators.LINEAR
duration = retractDuration
addUpdateListener { animator ->
- rippleShader.color = ColorUtils.setAlphaComponent(
- rippleShader.color,
+ dwellShader.color = ColorUtils.setAlphaComponent(
+ dwellShader.color,
animator.animatedValue as Int
)
invalidate()
@@ -127,13 +150,12 @@
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
dwellPulseOutAnimator?.cancel()
- rippleShader.shouldFadeOutRipple = false
- visibility = VISIBLE
+ drawDwell = true
}
override fun onAnimationEnd(animation: Animator?) {
- visibility = GONE
- resetRippleAlpha()
+ drawDwell = false
+ resetDwellAlpha()
}
})
start()
@@ -142,101 +164,54 @@
}
/**
- * Ripple that moves animates from an outer ripple ring of
- * startRadius => endRadius => expandedRadius
+ * Plays a ripple animation that grows to the dwellRadius with distortion.
*/
- fun startDwellRipple(
- startRadius: Float,
- endRadius: Float,
- expandedRadius: Float,
- isDozing: Boolean
- ) {
+ fun startDwellRipple(isDozing: Boolean) {
if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
return
}
- // we divide by 4 because the desired startRadius and endRadius is for the ripple's outer
- // ring see RippleShader
- val startDwellProgress = startRadius / radius / 4f
- val endInitialDwellProgress = endRadius / radius / 4f
- val endExpandDwellProgress = expandedRadius / radius / 4f
+ updateDwellRippleColor(isDozing)
- val alpha = if (isDozing) aodDwellAlpha else dwellAlpha
- val pulseOutEndAlpha = (255 * alpha).toInt()
- val expandDwellEndAlpha = kotlin.math.min((255 * (alpha + .25f)).toInt(), 255)
- val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(startDwellProgress,
- endInitialDwellProgress).apply {
- interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = if (isDozing) aodDwellPulseDuration else dwellPulseDuration
+ val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(0f, .8f).apply {
+ interpolator = Interpolators.LINEAR
+ duration = dwellPulseDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
+ dwellShader.progress = animator.animatedValue as Float
+ dwellShader.time = now.toFloat()
invalidate()
}
}
- val dwellPulseOutAlphaAnimator = ValueAnimator.ofInt(0, pulseOutEndAlpha).apply {
- interpolator = Interpolators.LINEAR
- duration = if (isDozing) aodDwellAlphaDuration else dwellAlphaDuration
- addUpdateListener { animator ->
- rippleShader.color = ColorUtils.setAlphaComponent(
- rippleShader.color,
- animator.animatedValue as Int
- )
- invalidate()
- }
- }
-
// slowly animate outwards until we receive a call to retractRipple or startUnlockedRipple
- val expandDwellRippleAnimator = ValueAnimator.ofFloat(endInitialDwellProgress,
- endExpandDwellProgress).apply {
+ val expandDwellRippleAnimator = ValueAnimator.ofFloat(.8f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
+ duration = dwellExpandDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
+ dwellShader.progress = animator.animatedValue as Float
+ dwellShader.time = now.toFloat()
invalidate()
}
}
- val expandDwellAlphaAnimator = ValueAnimator.ofInt(pulseOutEndAlpha, expandDwellEndAlpha)
- .apply {
- interpolator = Interpolators.LINEAR
- duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
- addUpdateListener { animator ->
- rippleShader.color = ColorUtils.setAlphaComponent(
- rippleShader.color,
- animator.animatedValue as Int
- )
- invalidate()
- }
- }
-
- val initialDwellPulseOutAnimator = AnimatorSet().apply {
- playTogether(dwellPulseOutRippleAnimator, dwellPulseOutAlphaAnimator)
- }
- val expandDwellAnimator = AnimatorSet().apply {
- playTogether(expandDwellRippleAnimator, expandDwellAlphaAnimator)
- }
-
dwellPulseOutAnimator = AnimatorSet().apply {
playSequentially(
- initialDwellPulseOutAnimator,
- expandDwellAnimator
+ dwellPulseOutRippleAnimator,
+ expandDwellRippleAnimator
)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
retractAnimator?.cancel()
- rippleShader.shouldFadeOutRipple = false
visibility = VISIBLE
+ drawDwell = true
}
override fun onAnimationEnd(animation: Animator?) {
- visibility = GONE
+ drawDwell = false
resetRippleAlpha()
}
})
@@ -252,16 +227,7 @@
return // Ignore if ripple effect is already playing
}
- var rippleStart = 0f
- var alphaDuration = alphaInDuration
- if (dwellPulseOutAnimator?.isRunning == true || retractAnimator?.isRunning == true) {
- rippleStart = rippleShader.progress
- alphaDuration = 0
- dwellPulseOutAnimator?.cancel()
- retractAnimator?.cancel()
- }
-
- val rippleAnimator = ValueAnimator.ofFloat(rippleStart, 1f).apply {
+ val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
duration = AuthRippleController.RIPPLE_ANIMATION_DURATION
addUpdateListener { animator ->
@@ -274,7 +240,7 @@
}
val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply {
- duration = alphaDuration
+ duration = alphaInDuration
addUpdateListener { animator ->
rippleShader.color = ColorUtils.setAlphaComponent(
rippleShader.color,
@@ -293,12 +259,14 @@
override fun onAnimationStart(animation: Animator?) {
unlockedRippleInProgress = true
rippleShader.shouldFadeOutRipple = true
+ drawRipple = true
visibility = VISIBLE
}
override fun onAnimationEnd(animation: Animator?) {
onAnimationEnd?.run()
unlockedRippleInProgress = false
+ drawRipple = false
visibility = GONE
}
})
@@ -313,17 +281,42 @@
)
}
- fun setColor(color: Int) {
- rippleShader.color = color
+ fun setLockScreenColor(color: Int) {
+ lockScreenColorVal = color
+ rippleShader.color = lockScreenColorVal
resetRippleAlpha()
}
+ fun updateDwellRippleColor(isDozing: Boolean) {
+ if (isDozing) {
+ dwellShader.color = Color.WHITE
+ } else {
+ dwellShader.color = lockScreenColorVal
+ }
+ resetDwellAlpha()
+ }
+
+ fun resetDwellAlpha() {
+ dwellShader.color = ColorUtils.setAlphaComponent(
+ dwellShader.color,
+ 255
+ )
+ }
+
override fun onDraw(canvas: Canvas?) {
// To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
// the active effect area. Values here should be kept in sync with the
// animation implementation in the ripple shader.
- val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * radius * 2f
- canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
+ if (drawDwell) {
+ val maskRadius = (1 - (1 - dwellShader.progress) * (1 - dwellShader.progress) *
+ (1 - dwellShader.progress)) * dwellRadius * 2f
+ canvas?.drawCircle(dwellOrigin.x, dwellOrigin.y, maskRadius, dwellPaint)
+ }
+
+ if (drawRipple) {
+ val mask = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * radius * 2f
+ canvas?.drawCircle(origin.x, origin.y, mask, ripplePaint)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index fb4616a..73e3aec 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -23,6 +23,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.util.ViewController;
@@ -44,6 +45,7 @@
extends ViewController<T> implements Dumpable {
@NonNull final StatusBarStateController mStatusBarStateController;
@NonNull final PanelExpansionStateManager mPanelExpansionStateManager;
+ @NonNull final SystemUIDialogManager mDialogManager;
@NonNull final DumpManager mDumpManger;
boolean mNotificationShadeVisible;
@@ -52,10 +54,12 @@
T view,
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
+ @NonNull SystemUIDialogManager dialogManager,
@NonNull DumpManager dumpManager) {
super(view);
mStatusBarStateController = statusBarStateController;
mPanelExpansionStateManager = panelExpansionStateManager;
+ mDialogManager = dialogManager;
mDumpManger = dumpManager;
}
@@ -64,12 +68,14 @@
@Override
protected void onViewAttached() {
mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
+ mDialogManager.registerListener(mDialogListener);
mDumpManger.registerDumpable(getDumpTag(), this);
}
@Override
protected void onViewDetached() {
mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
+ mDialogManager.unregisterListener(mDialogListener);
mDumpManger.unregisterDumpable(getDumpTag());
}
@@ -95,7 +101,8 @@
* authentication.
*/
boolean shouldPauseAuth() {
- return mNotificationShadeVisible;
+ return mNotificationShadeVisible
+ || mDialogManager.shouldHideAffordance();
}
/**
@@ -189,4 +196,7 @@
updatePauseAuth();
}
};
+
+ private final SystemUIDialogManager.Listener mDialogListener =
+ (shouldHide) -> updatePauseAuth();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
index 894b295..3732100 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -20,6 +20,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
@@ -30,8 +31,10 @@
@NonNull UdfpsBpView view,
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
+ @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager,
+ systemUIDialogManager, dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9ce0e94..3e9d6b0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -47,7 +47,6 @@
import android.os.Trace;
import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -71,6 +70,7 @@
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -103,6 +103,7 @@
public class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController";
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
+ private static final long DEFAULT_VIBRATION_DURATION = 1000; // milliseconds
// Minimum required delay between consecutive touch logs in milliseconds.
private static final long MIN_TOUCH_LOG_INTERVAL = 50;
@@ -118,6 +119,7 @@
@NonNull private final KeyguardStateController mKeyguardStateController;
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@NonNull private final DumpManager mDumpManager;
+ @NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Nullable private final Vibrator mVibrator;
@NonNull private final FalsingManager mFalsingManager;
@@ -163,7 +165,6 @@
private boolean mOnFingerDown;
private boolean mAttemptedToDismissKeyguard;
private Set<Callback> mCallbacks = new HashSet<>();
- private final VibrationEffect mLowTick;
@VisibleForTesting
public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
@@ -279,9 +280,6 @@
return;
}
mGoodCaptureReceived = true;
- if (mVibrator != null) {
- mVibrator.cancel();
- }
mView.stopIllumination();
if (mServerRequest != null) {
mServerRequest.onAcquiredGood();
@@ -324,12 +322,23 @@
}
}
- private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
+ /**
+ * Calculate the pointer speed given a velocity tracker and the pointer id.
+ * This assumes that the velocity tracker has already been passed all relevant motion events.
+ */
+ public static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
final float vx = tracker.getXVelocity(pointerId);
final float vy = tracker.getYVelocity(pointerId);
return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
}
+ /**
+ * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
+ */
+ public static boolean exceedsVelocityThreshold(float velocity) {
+ return velocity > 750f;
+ }
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -464,7 +473,7 @@
final float v = computePointerSpeed(mVelocityTracker, mActivePointerId);
final float minor = event.getTouchMinor(idx);
final float major = event.getTouchMajor(idx);
- final boolean exceedsVelocityThreshold = v > 750f;
+ final boolean exceedsVelocityThreshold = exceedsVelocityThreshold(v);
final String touchInfo = String.format(
"minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
minor, major, v, exceedsVelocityThreshold);
@@ -545,7 +554,8 @@
@Main Handler mainHandler,
@NonNull ConfigurationController configurationController,
@NonNull SystemClock systemClock,
- @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+ @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ @NonNull SystemUIDialogManager dialogManager) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -560,6 +570,7 @@
mKeyguardStateController = keyguardStateController;
mKeyguardViewManager = statusBarKeyguardViewManager;
mDumpManager = dumpManager;
+ mDialogManager = dialogManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mFalsingManager = falsingManager;
mPowerManager = powerManager;
@@ -572,7 +583,6 @@
mConfigurationController = configurationController;
mSystemClock = systemClock;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- mLowTick = lowTick();
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
@@ -607,27 +617,6 @@
udfpsHapticsSimulator.setUdfpsController(this);
}
- private VibrationEffect lowTick() {
- float tickIntensity = Settings.Global.getFloat(
- mContext.getContentResolver(), "low-tick-intensity", .5f);
- VibrationEffect.Composition composition = VibrationEffect.startComposition();
- composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
- tickIntensity, 0);
- int tickDelay = Settings.Global.getInt(
- mContext.getContentResolver(), "low-tick-delay", 25);
- int primitives = 1000 / tickDelay;
- float[] rampUp = new float[]{.48f, .58f, .69f, .83f};
- for (int i = 0; i < rampUp.length; i++) {
- composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
- tickIntensity * rampUp[i], tickDelay);
- }
- for (int i = rampUp.length; i < primitives; i++) {
- composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
- tickIntensity, tickDelay);
- }
- return composition.compose();
- }
-
/**
* Play haptic to signal udfps scanning started.
*/
@@ -637,8 +626,8 @@
mVibrator.vibrate(
Process.myUid(),
mContext.getOpPackageName(),
- mLowTick,
- "udfps-onStart-tick",
+ EFFECT_CLICK,
+ "udfps-onStart-click",
VIBRATION_SONIFICATION_ATTRIBUTES);
}
}
@@ -830,6 +819,7 @@
mServerRequest.mEnrollHelper,
mStatusBarStateController,
mPanelExpansionStateManager,
+ mDialogManager,
mDumpManager
);
case BiometricOverlayConstants.REASON_AUTH_KEYGUARD:
@@ -848,6 +838,7 @@
mSystemClock,
mKeyguardStateController,
mUnlockedScreenOffAnimationController,
+ mDialogManager,
this
);
case BiometricOverlayConstants.REASON_AUTH_BP:
@@ -858,6 +849,7 @@
bpView,
mStatusBarStateController,
mPanelExpansionStateManager,
+ mDialogManager,
mDumpManager
);
case BiometricOverlayConstants.REASON_AUTH_OTHER:
@@ -869,6 +861,7 @@
authOtherView,
mStatusBarStateController,
mPanelExpansionStateManager,
+ mDialogManager,
mDumpManager
);
default:
@@ -1025,7 +1018,6 @@
}
}
mOnFingerDown = false;
- mVibrator.cancel();
if (mView.isIlluminationRequested()) {
mView.stopIllumination();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 1f01fc5..ac38b13 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -20,9 +20,7 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -30,7 +28,6 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
-import android.util.TypedValue;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
@@ -38,7 +35,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
/**
@@ -106,9 +102,8 @@
mSensorOutlinePaint = new Paint(0 /* flags */);
mSensorOutlinePaint.setAntiAlias(true);
- mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon));
- mSensorOutlinePaint.setStyle(Paint.Style.STROKE);
- mSensorOutlinePaint.setStrokeWidth(2.f);
+ mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_moving_target_fill));
+ mSensorOutlinePaint.setStyle(Paint.Style.FILL);
mBlueFill = new Paint(0 /* flags */);
mBlueFill.setAntiAlias(true);
@@ -117,12 +112,12 @@
mMovingTargetFpIcon = context.getResources()
.getDrawable(R.drawable.ic_kg_fingerprint, null);
- mMovingTargetFpIcon.setTint(Color.WHITE);
+ mMovingTargetFpIcon.setTint(mContext.getColor(R.color.udfps_enroll_icon));
mMovingTargetFpIcon.mutate();
mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
- mHintColorFaded = getHintColorFaded(context);
+ mHintColorFaded = context.getColor(R.color.udfps_moving_target_fill);
mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress);
mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP);
mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP);
@@ -218,22 +213,6 @@
};
}
- @ColorInt
- private static int getHintColorFaded(@NonNull Context context) {
- final TypedValue tv = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
- final int alpha = (int) (tv.getFloat() * 255f);
-
- final int[] attrs = new int[] {android.R.attr.colorControlNormal};
- final TypedArray ta = context.obtainStyledAttributes(attrs);
- try {
- @ColorInt final int color = ta.getColor(0, context.getColor(R.color.white_disabled));
- return ColorUtils.setAlphaComponent(color, alpha);
- } finally {
- ta.recycle();
- }
- }
-
void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
mEnrollHelper = helper;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 79c7e66..631a461 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -18,12 +18,10 @@
import android.animation.ValueAnimator;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
@@ -80,24 +78,11 @@
mBackgroundPaint = new Paint();
mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
- mBackgroundPaint.setColor(context.getColor(R.color.white_disabled));
+ mBackgroundPaint.setColor(context.getColor(R.color.udfps_moving_target_fill));
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setStyle(Paint.Style.STROKE);
mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
- // Set background paint color and alpha.
- final int[] attrs = new int[] {android.R.attr.colorControlNormal};
- final TypedArray typedArray = context.obtainStyledAttributes(attrs);
- try {
- @ColorInt final int tintColor = typedArray.getColor(0, mBackgroundPaint.getColor());
- mBackgroundPaint.setColor(tintColor);
- } finally {
- typedArray.recycle();
- }
- TypedValue alpha = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
- mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f));
-
// Progress fill should *not* use the extracted system color.
mFillPaint = new Paint();
mFillPaint.setStrokeWidth(mStrokeWidthPx);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 292a904..ac9e92e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -22,6 +22,7 @@
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
@@ -54,8 +55,10 @@
@NonNull UdfpsEnrollHelper enrollHelper,
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
+ @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+ dumpManager);
mEnrollProgressBarRadius = getContext().getResources()
.getInteger(R.integer.config_udfpsEnrollProgressBar);
mEnrollHelper = enrollHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
index 6198733..97dca0f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -20,6 +20,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
@@ -33,8 +34,10 @@
@NonNull UdfpsFpmOtherView view,
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
+ @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+ dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 223eb78..3e8a568 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -86,8 +87,10 @@
@NonNull SystemClock systemClock,
@NonNull KeyguardStateController keyguardStateController,
@NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull UdfpsController udfpsController) {
- super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+ dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockScreenShadeTransitionController = transitionController;
@@ -217,6 +220,10 @@
return false;
}
+ if (mDialogManager.shouldHideAffordance()) {
+ return true;
+ }
+
if (mLaunchTransitionFadingAway) {
return true;
}
@@ -255,7 +262,6 @@
private void maybeShowInputBouncer() {
if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
mKeyguardViewManager.showBouncer(true);
- mKeyguardViewManager.resetAlternateAuth(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
index 71edbc0..d17eadd 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
@@ -19,6 +19,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE;
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
import android.provider.DeviceConfig;
@@ -71,7 +72,9 @@
return Result.passed(0);
}
- if (interactionType == LEFT_AFFORDANCE || interactionType == RIGHT_AFFORDANCE) {
+ if (interactionType == LEFT_AFFORDANCE
+ || interactionType == RIGHT_AFFORDANCE
+ || interactionType == LOCK_ICON) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index d3d6e03..6f30ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -18,6 +18,7 @@
import android.annotation.MainThread
import android.app.Dialog
+import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -88,7 +89,7 @@
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
if (cvh.usePanel()) {
- showDetail(cvh, control.getAppIntent().getIntent())
+ showDetail(cvh, control.getAppIntent())
} else {
cvh.action(CommandAction(templateId))
}
@@ -116,7 +117,7 @@
// Long press snould only be called when there is valid control state, otherwise ignore
cvh.cws.control?.let {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
- showDetail(cvh, it.getAppIntent().getIntent())
+ showDetail(cvh, it.getAppIntent())
}
}, false /* blockable */))
}
@@ -167,10 +168,10 @@
bgExecutor.execute { vibrator.vibrate(effect) }
}
- private fun showDetail(cvh: ControlViewHolder, intent: Intent) {
+ private fun showDetail(cvh: ControlViewHolder, pendingIntent: PendingIntent) {
bgExecutor.execute {
val activities: List<ResolveInfo> = context.packageManager.queryIntentActivities(
- intent,
+ pendingIntent.getIntent(),
PackageManager.MATCH_DEFAULT_ONLY
)
@@ -178,7 +179,7 @@
// make sure the intent is valid before attempting to open the dialog
if (activities.isNotEmpty() && taskViewFactory.isPresent) {
taskViewFactory.get().create(context, uiExecutor, {
- dialog = DetailDialog(activityContext, it, intent, cvh).also {
+ dialog = DetailDialog(activityContext, it, pendingIntent, cvh).also {
it.setOnDismissListener { _ -> dialog = null }
it.show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 8a47a36..4758ab0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -43,7 +43,7 @@
class DetailDialog(
val activityContext: Context?,
val taskView: TaskView,
- val intent: Intent,
+ val pendingIntent: PendingIntent,
val cvh: ControlViewHolder
) : Dialog(
activityContext ?: cvh.context,
@@ -59,6 +59,14 @@
var detailTaskId = INVALID_TASK_ID
+ private val fillInIntent = Intent().apply {
+ putExtra(EXTRA_USE_PANEL, true)
+
+ // Apply flags to make behaviour match documentLaunchMode=always.
+ addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+ addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ }
+
fun removeDetailTask() {
if (detailTaskId == INVALID_TASK_ID) return
ActivityTaskManager.getInstance().removeTask(detailTaskId)
@@ -67,13 +75,6 @@
val stateCallback = object : TaskView.Listener {
override fun onInitialized() {
- val launchIntent = Intent(intent)
- launchIntent.putExtra(EXTRA_USE_PANEL, true)
-
- // Apply flags to make behaviour match documentLaunchMode=always.
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
-
val options = activityContext?.let {
ActivityOptions.makeCustomAnimation(
it,
@@ -82,9 +83,8 @@
)
} ?: ActivityOptions.makeBasic()
taskView.startActivity(
- PendingIntent.getActivity(context, 0, launchIntent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE),
- null /* fillInIntent */,
+ pendingIntent,
+ fillInIntent,
options,
getTaskViewBounds()
)
@@ -97,6 +97,9 @@
override fun onTaskCreated(taskId: Int, name: ComponentName?) {
detailTaskId = taskId
+ requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
+ setAlpha(1f)
+ }
}
override fun onReleased() {
@@ -121,6 +124,7 @@
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
addView(taskView)
+ setAlpha(0f)
}
requireViewById<ImageView>(R.id.control_detail_close).apply {
@@ -134,7 +138,7 @@
removeDetailTask()
dismiss()
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
- v.context.startActivity(intent)
+ pendingIntent.send()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 5fdf026..d950d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -31,13 +31,14 @@
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -109,7 +110,10 @@
Builder setRecentTasks(Optional<RecentTasks> r);
@BindsInstance
- Builder setSizeCompatUI(Optional<SizeCompatUI> s);
+ Builder setCompatUI(Optional<CompatUI> s);
+
+ @BindsInstance
+ Builder setDragAndDrop(Optional<DragAndDrop> d);
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index fd3fcdd..12786f2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -66,7 +66,6 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -125,7 +124,6 @@
},
subcomponents = {
StatusBarComponent.class,
- StatusBarFragmentComponent.class,
NotificationRowComponent.class,
DozeComponent.class,
ExpandableNotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 543ba8f..b815d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -25,16 +25,17 @@
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.dagger.TvWMShellModule;
import com.android.wm.shell.dagger.WMShellModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -118,5 +119,8 @@
Optional<RecentTasks> getRecentTasks();
@WMSingleton
- SizeCompatUI getSizeCompatUI();
+ CompatUI getCompatUI();
+
+ @WMSingleton
+ DragAndDrop getDragAndDrop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 9c25b35..2beed4c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -40,7 +40,6 @@
void extendPulse(int reason);
void setAnimateWakeup(boolean animateWakeup);
- void setAnimateScreenOff(boolean animateScreenOff);
/**
* Reports that a tap event happend on the Sensors Low Power Island.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 374bed3..5677072 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -21,22 +21,17 @@
import android.app.AlarmManager;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.Handler;
import android.os.SystemClock;
-import android.provider.Settings;
import android.text.format.Formatter;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
@@ -50,8 +45,7 @@
* The policy controlling doze.
*/
@DozeScope
-public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
- ConfigurationController.ConfigurationListener {
+public class DozeUi implements DozeMachine.Part {
// if enabled, calls dozeTimeTick() whenever the time changes:
private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -65,19 +59,9 @@
private final DozeParameters mDozeParameters;
private final DozeLog mDozeLog;
private final Lazy<StatusBarStateController> mStatusBarStateController;
- private final TunerService mTunerService;
- private final ConfigurationController mConfigurationController;
-
- private boolean mKeyguardShowing;
private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
new KeyguardUpdateMonitorCallback() {
@Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- mKeyguardShowing = showing;
- updateAnimateScreenOff();
- }
-
- @Override
public void onTimeChanged() {
if (BURN_IN_TESTING_ENABLED && mStatusBarStateController != null
&& mStatusBarStateController.get().isDozing()) {
@@ -88,11 +72,6 @@
mHandler.post(mWakeLock.wrap(() -> {}));
}
}
-
- @Override
- public void onShadeExpandedChanged(boolean expanded) {
- updateAnimateScreenOff();
- }
};
private long mLastTimeTickElapsed = 0;
@@ -101,9 +80,7 @@
public DozeUi(Context context, AlarmManager alarmManager,
WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
- DozeLog dozeLog, TunerService tunerService,
- Lazy<StatusBarStateController> statusBarStateController,
- ConfigurationController configurationController) {
+ DozeLog dozeLog, Lazy<StatusBarStateController> statusBarStateController) {
mContext = context;
mWakeLock = wakeLock;
mHost = host;
@@ -113,19 +90,7 @@
mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
mDozeLog = dozeLog;
- mTunerService = tunerService;
mStatusBarStateController = statusBarStateController;
-
- mTunerService.addTunable(this, Settings.Secure.DOZE_ALWAYS_ON);
-
- mConfigurationController = configurationController;
- mConfigurationController.addCallback(this);
- }
-
- @Override
- public void destroy() {
- mTunerService.removeTunable(this);
- mConfigurationController.removeCallback(this);
}
@Override
@@ -133,21 +98,6 @@
mMachine = dozeMachine;
}
- /**
- * Decide if we're taking over the screen-off animation
- * when the device was configured to skip doze after screen off.
- */
- private void updateAnimateScreenOff() {
- if (mCanAnimateTransition) {
- final boolean controlScreenOff =
- mDozeParameters.getAlwaysOn()
- && (mKeyguardShowing || mDozeParameters.shouldControlUnlockedScreenOff())
- && !mHost.isPowerSaveActive();
- mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
- mHost.setAnimateScreenOff(controlScreenOff);
- }
- }
-
private void pulseWhileDozing(int reason) {
mHost.pulseWhileDozing(
new DozeHost.PulseCallback() {
@@ -276,21 +226,4 @@
scheduleTimeTick();
}
-
- @VisibleForTesting
- KeyguardUpdateMonitorCallback getKeyguardCallback() {
- return mKeyguardVisibilityCallback;
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) {
- updateAnimateScreenOff();
- }
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- updateAnimateScreenOff();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index b45dc52..10878dc 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -42,7 +42,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
@@ -308,22 +307,22 @@
return instantiateWithInjections(context, className, arguments);
}
- private Fragment instantiateWithInjections(Context context, String className,
- Bundle args) {
- Method method = mManager.getInjectionMap().get(className);
- if (method != null) {
+ private Fragment instantiateWithInjections(
+ Context context, String className, Bundle args) {
+ FragmentService.FragmentInstantiationInfo fragmentInstantiationInfo =
+ mManager.getInjectionMap().get(className);
+ if (fragmentInstantiationInfo != null) {
try {
- Fragment f = (Fragment) method.invoke(mManager.getFragmentCreator());
+ Fragment f = (Fragment) fragmentInstantiationInfo
+ .mMethod
+ .invoke(fragmentInstantiationInfo.mDaggerComponent);
// Setup the args, taken from Fragment#instantiate.
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.setArguments(args);
}
return f;
- } catch (IllegalAccessException e) {
- throw new Fragment.InstantiationException("Unable to instantiate " + className,
- e);
- } catch (InvocationTargetException e) {
+ } catch (IllegalAccessException | InvocationTargetException e) {
throw new Fragment.InstantiationException("Unable to instantiate " + className,
e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index 7f57fcc..2a5e653 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -18,13 +18,13 @@
import android.content.res.Configuration;
import android.os.Handler;
import android.util.ArrayMap;
+import android.util.Log;
import android.view.View;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.qs.QSFragment;
-import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.FileDescriptor;
@@ -46,9 +46,14 @@
private static final String TAG = "FragmentService";
private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
- private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
+ /**
+ * A map with the means to create fragments via Dagger injection.
+ *
+ * key: the fragment class name.
+ * value: see {@link FragmentInstantiationInfo}.
+ */
+ private final ArrayMap<String, FragmentInstantiationInfo> mInjectionMap = new ArrayMap<>();
private final Handler mHandler = new Handler();
- private final FragmentCreator mFragmentCreator;
private ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -65,26 +70,31 @@
FragmentCreator.Factory fragmentCreatorFactory,
ConfigurationController configurationController,
DumpManager dumpManager) {
- mFragmentCreator = fragmentCreatorFactory.build();
- initInjectionMap();
+ addFragmentInstantiationProvider(fragmentCreatorFactory.build());
configurationController.addCallback(mConfigurationListener);
dumpManager.registerDumpable(getClass().getSimpleName(), this);
}
- ArrayMap<String, Method> getInjectionMap() {
+ ArrayMap<String, FragmentInstantiationInfo> getInjectionMap() {
return mInjectionMap;
}
- FragmentCreator getFragmentCreator() {
- return mFragmentCreator;
- }
-
- private void initInjectionMap() {
- for (Method method : FragmentCreator.class.getDeclaredMethods()) {
+ /**
+ * Adds a new Dagger component object that provides method(s) to create fragments via injection.
+ */
+ public void addFragmentInstantiationProvider(Object daggerComponent) {
+ for (Method method : daggerComponent.getClass().getDeclaredMethods()) {
if (Fragment.class.isAssignableFrom(method.getReturnType())
&& (method.getModifiers() & Modifier.PUBLIC) != 0) {
- mInjectionMap.put(method.getReturnType().getName(), method);
+ String fragmentName = method.getReturnType().getName();
+ if (mInjectionMap.containsKey(fragmentName)) {
+ Log.w(TAG, "Fragment " + fragmentName + " is already provided by different"
+ + " Dagger component; Not adding method");
+ continue;
+ }
+ mInjectionMap.put(
+ fragmentName, new FragmentInstantiationInfo(method, daggerComponent));
}
}
}
@@ -134,9 +144,6 @@
* Inject a QSFragment.
*/
QSFragment createQSFragment();
-
- /** Inject a CollapsedStatusBarFragment. */
- CollapsedStatusBarFragment createCollapsedStatusBarFragment();
}
private class FragmentHostState {
@@ -161,4 +168,16 @@
mFragmentHostManager.onConfigurationChanged(newConfig);
}
}
+
+ /** An object containing the information needed to instantiate a fragment. */
+ static class FragmentInstantiationInfo {
+ /** The method that returns a newly-created fragment of the given class. */
+ final Method mMethod;
+ /** The Dagger component that the method should be invoked on. */
+ final Object mDaggerComponent;
+ FragmentInstantiationInfo(Method method, Object daggerComponent) {
+ this.mMethod = method;
+ this.mDaggerComponent = daggerComponent;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index ad47232..2ebcd853 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -31,8 +31,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -56,6 +54,7 @@
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -80,8 +79,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -112,7 +109,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
-import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
@@ -123,6 +120,8 @@
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -238,7 +237,9 @@
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
private final Optional<StatusBar> mStatusBarOptional;
+ private final SystemUIDialogManager mDialogManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -343,10 +344,12 @@
UiEventLogger uiEventLogger,
RingerModeTracker ringerModeTracker,
SysUiState sysUiState,
- @Main Handler handler,
+ @Main Handler handler,
PackageManager packageManager,
Optional<StatusBar> statusBarOptional,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DialogLaunchAnimator dialogLaunchAnimator,
+ SystemUIDialogManager dialogManager) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -377,6 +380,8 @@
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
+ mDialogManager = dialogManager;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -435,11 +440,14 @@
}
/**
- * Show the global actions dialog (creating if necessary)
+ * Show the global actions dialog (creating if necessary) or hide it if it's already showing.
*
- * @param keyguardShowing True if keyguard is showing
+ * @param keyguardShowing True if keyguard is showing
+ * @param isDeviceProvisioned True if device is provisioned
+ * @param view The view from which we should animate the dialog when showing it
*/
- public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
+ public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
+ @Nullable View view) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null && mDialog.isShowing()) {
@@ -451,7 +459,7 @@
mDialog.dismiss();
mDialog = null;
} else {
- handleShow();
+ handleShow(view);
}
}
@@ -483,7 +491,7 @@
}
}
- protected void handleShow() {
+ protected void handleShow(@Nullable View view) {
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
@@ -493,8 +501,13 @@
attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mDialog.getWindow().setAttributes(attrs);
// Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
- mDialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);
- mDialog.show();
+ mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
+
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(mDialog, view);
+ } else {
+ mDialog.show();
+ }
mWindowManagerFuncs.onGlobalActionsShown();
}
@@ -643,7 +656,7 @@
}
}
- protected void onRotate() {
+ protected void onRefresh() {
// re-allocate actions between main and overflow lists
this.createActionItems();
}
@@ -667,8 +680,9 @@
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
+ mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
+ mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils,
+ mDialogManager);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -702,14 +716,6 @@
}
@Override
- public void onUiModeChanged() {
- mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
- if (mDialog != null && mDialog.isShowing()) {
- mDialog.refreshDialog();
- }
- }
-
- @Override
public void onConfigChanged(Configuration newConfig) {
if (mDialog != null && mDialog.isShowing()
&& (newConfig.smallestScreenWidthDp != mSmallestScreenWidthDp)) {
@@ -717,6 +723,7 @@
mDialog.refreshDialog();
}
}
+
/**
* Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
* called when the quick access wallet requests dismissal.
@@ -1363,6 +1370,10 @@
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1379,6 +1390,10 @@
if (mDialog != null) {
// don't dismiss the dialog if we're opening the power options menu
if (!(item instanceof PowerOptionsAction)) {
+ // Usually clicking an item shuts down the phone, locks, or starts an
+ // activity. We don't want to animate back into the power button when that
+ // happens, so we disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
}
} else {
@@ -1446,6 +1461,10 @@
final Action action = getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1459,6 +1478,10 @@
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -1510,6 +1533,10 @@
final Action action = getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1523,6 +1550,10 @@
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -2066,7 +2097,9 @@
case MESSAGE_DISMISS:
if (mDialog != null) {
if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
- mDialog.completeDismiss();
+ // Hide instantly.
+ mDialog.hide();
+ mDialog.dismiss();
} else {
mDialog.dismiss();
}
@@ -2113,7 +2146,7 @@
}
@VisibleForTesting
- static class ActionsDialogLite extends Dialog implements DialogInterface,
+ static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
ColorExtractor.OnColorsChangedListener {
protected final Context mContext;
@@ -2126,13 +2159,12 @@
protected Drawable mBackgroundDrawable;
protected final SysuiColorExtractor mColorExtractor;
private boolean mKeyguardShowing;
- protected boolean mShowing;
protected float mScrimAlpha;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
protected final SysUiState mSysUiState;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
- protected final Runnable mOnRotateCallback;
+ protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
private Optional<StatusBar> mStatusBarOptional;
@@ -2151,7 +2183,7 @@
}
@Override
- public boolean onSingleTapConfirmed(MotionEvent e) {
+ public boolean onSingleTapUp(MotionEvent e) {
// Close without opening shade
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
cancel();
@@ -2189,11 +2221,15 @@
MyOverflowAdapter overflowAdapter,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
+ SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
Optional<StatusBar> statusBarOptional,
- KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
- super(context, themeRes);
+ KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
+ SystemUIDialogManager systemUiDialogManager) {
+ // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
+ // dismiss this dialog when the device is locked.
+ super(context, themeRes, false /* dismissOnDeviceLock */,
+ systemUiDialogManager);
mContext = context;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
@@ -2202,36 +2238,32 @@
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mSysUiState = sysuiState;
- mOnRotateCallback = onRotateCallback;
+ mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
-
mGestureDetector = new GestureDetector(mContext, mGestureListener);
+ }
- // Window initialization
- Window window = getWindow();
- window.requestFeature(Window.FEATURE_NO_TITLE);
- // Inflate the decor view, so the attributes below are not overwritten by the theme.
- window.getDecorView();
- window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- window.setLayout(MATCH_PARENT, MATCH_PARENT);
- window.addFlags(
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- window.getAttributes().setFitInsetsTypes(0 /* types */);
- setTitle(R.string.global_actions);
-
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
initializeLayout();
}
@Override
+ protected int getWidth() {
+ return MATCH_PARENT;
+ }
+
+ @Override
+ protected int getHeight() {
+ return MATCH_PARENT;
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
}
@@ -2371,7 +2403,8 @@
message.animate()
.alpha(0f)
.setDuration(TOAST_FADE_TIME)
- .setStartDelay(visibleTime);
+ .setStartDelay(visibleTime)
+ .setListener(null);
}
});
}
@@ -2423,122 +2456,32 @@
@Override
public void show() {
super.show();
- // split this up so we can override but still call Dialog.show
- showDialog();
- }
-
- protected void showDialog() {
- mShowing = true;
mNotificationShadeWindowController.setRequestTopUi(true, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
.commitUpdate(mContext.getDisplayId());
-
- ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
- root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
- root.setPadding(windowInsets.getStableInsetLeft(),
- windowInsets.getStableInsetTop(),
- windowInsets.getStableInsetRight(),
- windowInsets.getStableInsetBottom());
- return WindowInsets.CONSUMED;
- });
-
- mBackgroundDrawable.setAlpha(0);
- float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
- ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f);
- alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- alphaAnimator.setDuration(183);
- alphaAnimator.addUpdateListener((animation) -> {
- float animatedValue = animation.getAnimatedFraction();
- int alpha = (int) (animatedValue * mScrimAlpha * 255);
- mBackgroundDrawable.setAlpha(alpha);
- });
-
- ObjectAnimator xAnimator =
- ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f);
- xAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- xAnimator.setDuration(350);
-
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(alphaAnimator, xAnimator);
- animatorSet.start();
}
@Override
public void dismiss() {
- dismissWithAnimation(() -> {
- dismissInternal();
- });
- }
+ dismissOverflow();
+ dismissPowerOptions();
- protected void dismissInternal() {
- mContainer.setTranslationX(0);
- ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "alpha", 1f, 0f);
- alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- alphaAnimator.setDuration(233);
- alphaAnimator.addUpdateListener((animation) -> {
- float animatedValue = 1f - animation.getAnimatedFraction();
- int alpha = (int) (animatedValue * mScrimAlpha * 255);
- mBackgroundDrawable.setAlpha(alpha);
- });
-
- float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
- ObjectAnimator xAnimator =
- ObjectAnimator.ofFloat(mContainer, "translationX", 0f, xOffset);
- xAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- xAnimator.setDuration(350);
-
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(alphaAnimator, xAnimator);
- animatorSet.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- completeDismiss();
- }
- });
-
- animatorSet.start();
-
- // close first, as popup windows will not fade during the animation
- dismissOverflow(false);
- dismissPowerOptions(false);
- }
-
- void dismissWithAnimation(Runnable animation) {
- if (!mShowing) {
- return;
- }
- mShowing = false;
- animation.run();
- }
-
- protected void completeDismiss() {
- mShowing = false;
- dismissOverflow(true);
- dismissPowerOptions(true);
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
+
super.dismiss();
}
- protected final void dismissOverflow(boolean immediate) {
+ protected final void dismissOverflow() {
if (mOverflowPopup != null) {
- if (immediate) {
- mOverflowPopup.dismissImmediate();
- } else {
- mOverflowPopup.dismiss();
- }
+ mOverflowPopup.dismiss();
}
}
- protected final void dismissPowerOptions(boolean immediate) {
+ protected final void dismissPowerOptions() {
if (mPowerOptionsDialog != null) {
- if (immediate) {
- mPowerOptionsDialog.dismiss();
- } else {
- mPowerOptionsDialog.dismiss();
- }
+ mPowerOptionsDialog.dismiss();
}
}
@@ -2574,20 +2517,18 @@
}
public void refreshDialog() {
- // ensure dropdown menus are dismissed before re-initializing the dialog
- dismissOverflow(true);
- dismissPowerOptions(true);
+ mOnRefreshCallback.run();
- // re-create dialog
- initializeLayout();
+ // Dismiss the dropdown menus.
+ dismissOverflow();
+ dismissPowerOptions();
+
+ // Update the list as the max number of items per row has probably changed.
mGlobalActionsLayout.updateList();
}
public void onRotate(int from, int to) {
- if (mShowing) {
- mOnRotateCallback.run();
- refreshDialog();
- }
+ refreshDialog();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c4508e0..96ae646 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -82,7 +82,7 @@
if (mDisabled) return;
mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
- mDeviceProvisionedController.isDeviceProvisioned());
+ mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index a424674..d73d9cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.os.SystemClock;
import android.text.TextUtils;
import androidx.annotation.IntDef;
@@ -40,13 +41,24 @@
import java.util.Map;
/**
- * Rotates through messages to show on the keyguard bottom area on the lock screen
- * NOTE: This controller should not be used on AoD to avoid waking up the AP too often.
+ * Animates through messages to show on the keyguard bottom area on the lock screen.
+ * Utilizes a {@link KeyguardIndicationTextView} for animations. This class handles the rotating
+ * nature of the messages including:
+ * - ensuring a message is shown for its minimum amount of time. Minimum time is determined by
+ * {@link KeyguardIndication#getMinVisibilityMillis()}
+ * - showing the next message after a default of 3.5 seconds before animating to the next
+ * - statically showing a single message if there is only one message to show
+ * - showing certain messages immediately, assuming te current message has been shown for
+ * at least {@link KeyguardIndication#getMinVisibilityMillis()}. For example, transient and
+ * biometric messages are meant to be shown immediately.
+ * - ending animations when dozing begins, and resuming when dozing ends. Rotating messages on
+ * AoD is undesirable since it wakes up the AP too often.
*/
public class KeyguardIndicationRotateTextViewController extends
ViewController<KeyguardIndicationTextView> implements Dumpable {
public static String TAG = "KgIndicationRotatingCtrl";
private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds
+ public static final long IMPORTANT_MSG_MIN_DURATION = 2000L + 600L; // 2000ms + [Y in duration]
private final StatusBarStateController mStatusBarStateController;
private final float mMaxAlpha;
@@ -62,6 +74,8 @@
// List of indication types to show. The next indication to show is always at index 0
private final List<Integer> mIndicationQueue = new LinkedList<>();
private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE;
+ private CharSequence mCurrMessage;
+ private long mLastIndicationSwitch;
private boolean mIsDozing;
@@ -94,17 +108,19 @@
* Update the indication type with the given String.
* @param type of indication
* @param newIndication message to associate with this indication type
- * @param showImmediately if true: shows this indication message immediately. Else, the text
- * associated with this type is updated and will show when its turn in
- * the IndicationQueue comes around.
+ * @param showAsap if true: shows this indication message as soon as possible. If false,
+ * the text associated with this type is updated and will show when its turn
+ * in the IndicationQueue comes around.
*/
public void updateIndication(@IndicationType int type, KeyguardIndication newIndication,
- boolean updateImmediately) {
+ boolean showAsap) {
if (type == INDICATION_TYPE_REVERSE_CHARGING) {
// temporarily don't show here, instead use AmbientContainer b/181049781
return;
}
- final boolean hasPreviousIndication = mIndicationMessages.get(type) != null;
+ long minShowDuration = getMinVisibilityMillis(mIndicationMessages.get(mCurrIndicationType));
+ final boolean hasPreviousIndication = mIndicationMessages.get(type) != null
+ && !TextUtils.isEmpty(mIndicationMessages.get(type).getMessage());
final boolean hasNewIndication = newIndication != null;
if (!hasNewIndication) {
mIndicationMessages.remove(type);
@@ -121,25 +137,46 @@
return;
}
- final boolean showNow = updateImmediately
- || mCurrIndicationType == INDICATION_TYPE_NONE
- || mCurrIndicationType == type;
+ long currTime = SystemClock.uptimeMillis();
+ long timeSinceLastIndicationSwitch = currTime - mLastIndicationSwitch;
+ boolean currMsgShownForMinTime = timeSinceLastIndicationSwitch >= minShowDuration;
if (hasNewIndication) {
- if (showNow) {
+ if (mCurrIndicationType == INDICATION_TYPE_NONE || mCurrIndicationType == type) {
showIndication(type);
+ } else if (showAsap) {
+ if (currMsgShownForMinTime) {
+ showIndication(type);
+ } else {
+ mIndicationQueue.removeIf(x -> x == type);
+ mIndicationQueue.add(0 /* index */, type /* type */);
+ scheduleShowNextIndication(minShowDuration - timeSinceLastIndicationSwitch);
+ }
} else if (!isNextIndicationScheduled()) {
- scheduleShowNextIndication();
+ long nextShowTime = Math.max(
+ getMinVisibilityMillis(mIndicationMessages.get(type)),
+ DEFAULT_INDICATION_SHOW_LENGTH);
+ if (timeSinceLastIndicationSwitch >= nextShowTime) {
+ showIndication(type);
+ } else {
+ scheduleShowNextIndication(
+ nextShowTime - timeSinceLastIndicationSwitch);
+ }
}
return;
}
+ // current indication is updated to empty
if (mCurrIndicationType == type
&& !hasNewIndication
- && updateImmediately) {
- if (mShowNextIndicationRunnable != null) {
- mShowNextIndicationRunnable.runImmediately();
+ && showAsap) {
+ if (currMsgShownForMinTime) {
+ if (mShowNextIndicationRunnable != null) {
+ mShowNextIndicationRunnable.runImmediately();
+ } else {
+ showIndication(INDICATION_TYPE_NONE);
+ }
} else {
- showIndication(INDICATION_TYPE_NONE);
+ scheduleShowNextIndication(minShowDuration - timeSinceLastIndicationSwitch);
}
}
}
@@ -164,11 +201,10 @@
* - will continue to be in the rotation of messages shown until hideTransient is called.
*/
public void showTransient(CharSequence newIndication) {
- final long inAnimationDuration = 600L; // see KeyguardIndicationTextView.getYInDuration
updateIndication(INDICATION_TYPE_TRANSIENT,
new KeyguardIndication.Builder()
.setMessage(newIndication)
- .setMinVisibilityMillis(2000L + inAnimationDuration)
+ .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
.setTextColor(mInitialTextColorState)
.build(),
/* showImmediately */true);
@@ -189,6 +225,15 @@
}
/**
+ * Clears all messages in the queue and sets the current message to an empty string.
+ */
+ public void clearMessages() {
+ mCurrIndicationType = INDICATION_TYPE_NONE;
+ mIndicationQueue.clear();
+ mView.clearMessages();
+ }
+
+ /**
* Immediately show the passed indication type and schedule the next indication to show.
* Will re-add this indication to be re-shown after all other indications have been
* rotated through.
@@ -196,27 +241,52 @@
private void showIndication(@IndicationType int type) {
cancelScheduledIndication();
+ final CharSequence previousMessage = mCurrMessage;
+ final @IndicationType int previousIndicationType = mCurrIndicationType;
mCurrIndicationType = type;
+ mCurrMessage = mIndicationMessages.get(type) != null
+ ? mIndicationMessages.get(type).getMessage()
+ : null;
+
mIndicationQueue.removeIf(x -> x == type);
if (mCurrIndicationType != INDICATION_TYPE_NONE) {
mIndicationQueue.add(type); // re-add to show later
}
- mView.switchIndication(mIndicationMessages.get(type));
+ mLastIndicationSwitch = SystemClock.uptimeMillis();
+ if (!TextUtils.equals(previousMessage, mCurrMessage)
+ || previousIndicationType != mCurrIndicationType) {
+ mView.switchIndication(mIndicationMessages.get(type));
+ }
// only schedule next indication if there's more than just this indication in the queue
if (mCurrIndicationType != INDICATION_TYPE_NONE && mIndicationQueue.size() > 1) {
- scheduleShowNextIndication();
+ scheduleShowNextIndication(Math.max(
+ getMinVisibilityMillis(mIndicationMessages.get(type)),
+ DEFAULT_INDICATION_SHOW_LENGTH));
}
}
+ private long getMinVisibilityMillis(KeyguardIndication indication) {
+ if (indication == null) {
+ return 0;
+ }
+
+ if (indication.getMinVisibilityMillis() == null) {
+ return 0;
+ }
+
+ return indication.getMinVisibilityMillis();
+ }
+
protected boolean isNextIndicationScheduled() {
return mShowNextIndicationRunnable != null;
}
- private void scheduleShowNextIndication() {
+
+ private void scheduleShowNextIndication(long msUntilShowNextMsg) {
cancelScheduledIndication();
- mShowNextIndicationRunnable = new ShowNextIndication(DEFAULT_INDICATION_SHOW_LENGTH);
+ mShowNextIndicationRunnable = new ShowNextIndication(msUntilShowNextMsg);
}
private void cancelScheduledIndication() {
@@ -292,7 +362,9 @@
}
}
+ // only used locally to stop showing any messages & stop the rotating messages
static final int INDICATION_TYPE_NONE = -1;
+
public static final int INDICATION_TYPE_OWNER_INFO = 0;
public static final int INDICATION_TYPE_DISCLOSURE = 1;
public static final int INDICATION_TYPE_LOGOUT = 2;
@@ -303,6 +375,7 @@
public static final int INDICATION_TYPE_RESTING = 7;
public static final int INDICATION_TYPE_USER_LOCKED = 8;
public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
+ public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11;
@IntDef({
INDICATION_TYPE_NONE,
@@ -316,6 +389,7 @@
INDICATION_TYPE_RESTING,
INDICATION_TYPE_USER_LOCKED,
INDICATION_TYPE_REVERSE_CHARGING,
+ INDICATION_TYPE_BIOMETRIC_MESSAGE
})
@Retention(RetentionPolicy.SOURCE)
public @interface IndicationType{}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 2cc564b..fabe92d2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -24,6 +24,7 @@
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
+import androidx.annotation.VisibleForTesting
import androidx.core.math.MathUtils
import com.android.internal.R
import com.android.keyguard.KeyguardClockSwitchController
@@ -32,6 +33,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -91,7 +93,8 @@
private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController,
private val smartspaceTransitionController: SmartspaceTransitionController,
- private val featureFlags: FeatureFlags
+ private val featureFlags: FeatureFlags,
+ private val biometricUnlockController: BiometricUnlockController
) : KeyguardStateController.Callback {
/**
@@ -105,7 +108,8 @@
* If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned
* animation is started in [notifyStartKeyguardExitAnimation].
*/
- private var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
+ @VisibleForTesting
+ var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
private var surfaceBehindRemoteAnimationStartTime: Long = 0
@@ -132,7 +136,8 @@
* Animator that animates in the surface behind the keyguard. This is used to play a canned
* animation on the surface, if we're not doing a swipe gesture.
*/
- private val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
+ @VisibleForTesting
+ val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
/** Rounded corner radius to apply to the surface behind the keyguard. */
private var roundedCornerRadius = 0f
@@ -220,7 +225,18 @@
// to animate it in. Otherwise, the swipe touch events will continue animating it.
if (!requestedShowSurfaceBehindKeyguard) {
keyguardViewController.hide(startTime, 350)
- surfaceBehindEntryAnimator.start()
+
+ // If we're wake and unlocking, we don't want to animate the surface since we're going
+ // to do the light reveal scrim from the black AOD screen. Make it visible and end the
+ // remote aimation.
+ if (biometricUnlockController.isWakeAndUnlock) {
+ setSurfaceBehindAppearAmount(1f)
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ } else {
+ // Otherwise, animate it in normally.
+ surfaceBehindEntryAnimator.start()
+ }
}
// Finish the keyguard remote animation if the dismiss amount has crossed the threshold.
@@ -264,7 +280,7 @@
* animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
* cancelled).
*/
- private fun setSurfaceBehindAppearAmount(amount: Float) {
+ fun setSurfaceBehindAppearAmount(amount: Float) {
if (surfaceBehindRemoteAnimationTarget == null) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7f9eae8..7a53fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -793,7 +793,8 @@
return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
} else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
- } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
+ } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
+ || mUpdateMonitor.isFingerprintLockedOut())) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
@@ -1024,7 +1025,10 @@
if (!mExternallyEnabled) {
hideLocked();
}
- } else if (mShowing) {
+ } else if (mShowing && !mKeyguardStateController.isKeyguardGoingAway()) {
+ // If we are going to sleep but the keyguard is showing (and will continue to be
+ // showing, not in the process of going away) then reset its state. Otherwise, let
+ // this fall through and explicitly re-lock the keyguard.
mPendingReset = true;
} else if (
(offReason == WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT
@@ -2450,9 +2454,7 @@
if (mSurfaceBehindRemoteAnimationFinishedCallback != null) {
try {
- if (!cancelled) {
- mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
- }
+ mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
mSurfaceBehindRemoteAnimationFinishedCallback = null;
} catch (RemoteException e) {
e.printStackTrace();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index e87558e..b85f1072 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -37,7 +37,7 @@
private const val TAG = "MediaCarouselController"
private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
-private const val DEBUG = false
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
/**
* Class that is responsible for keeping the view carousel up to date.
@@ -209,41 +209,56 @@
oldKey: String?,
data: MediaData,
immediately: Boolean,
- isSsReactivated: Boolean
+ receivedSmartspaceCardLatency: Int
) {
if (addOrUpdatePlayer(key, oldKey, data)) {
// Log card received if a new resumable media card is added
MediaPlayerData.getMediaPlayer(key)?.let {
+ /* ktlint-disable max-line-length */
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId,
it.mUid,
/* isRecommendationCard */ false,
- it.surfaceForSmartspaceLogging,
+ intArrayOf(
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
rank = MediaPlayerData.getMediaPlayerIndex(key))
+ /* ktlint-disable max-line-length */
}
- }
- if (isSsReactivated) {
- // If resumable media is reactivated by headphone connection, update instance
- // id for each card and log a receive event.
+ if (mediaCarouselScrollHandler.visibleToUser &&
+ mediaCarouselScrollHandler.visibleMediaIndex
+ == MediaPlayerData.getMediaPlayerIndex(key)) {
+ logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
+ }
+ } else if (receivedSmartspaceCardLatency != 0) {
+ // Log resume card received if resumable media card is reactivated and
+ // resume card is ranked first
MediaPlayerData.players().forEachIndexed { index, it ->
if (it.recommendationViewHolder == null) {
it.mInstanceId = SmallHash.hash(it.mUid +
systemClock.currentTimeMillis().toInt())
+ it.mIsImpressed = false
+ /* ktlint-disable max-line-length */
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId,
it.mUid,
/* isRecommendationCard */ false,
- it.surfaceForSmartspaceLogging,
- rank = index)
+ intArrayOf(
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
+ rank = index,
+ receivedLatencyMillis = receivedSmartspaceCardLatency)
+ /* ktlint-disable max-line-length */
}
}
+ // If media container area already visible to the user, log impression for
+ // reactivated card.
+ if (mediaCarouselScrollHandler.visibleToUser &&
+ !mediaCarouselScrollHandler.qsExpanded) {
+ logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
+ }
}
- if (mediaCarouselScrollHandler.visibleToUser &&
- isSsReactivated && !mediaCarouselScrollHandler.qsExpanded) {
- // It could happen that reactived media player isn't visible to user because
- // of it is a resumption card.
- logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
- }
+
val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) {
// This view isn't playing, let's remove this! This happens e.g when
@@ -262,28 +277,51 @@
override fun onSmartspaceMediaDataLoaded(
key: String,
data: SmartspaceMediaData,
- shouldPrioritize: Boolean
+ shouldPrioritize: Boolean,
+ isSsReactivated: Boolean
) {
if (DEBUG) Log.d(TAG, "Loading Smartspace media update")
if (data.isActive) {
+ if (isSsReactivated && shouldPrioritize) {
+ // Log resume card received if resumable media card is reactivated and
+ // recommendation card is valid and ranked first
+ MediaPlayerData.players().forEachIndexed { index, it ->
+ if (it.recommendationViewHolder == null) {
+ it.mInstanceId = SmallHash.hash(it.mUid +
+ systemClock.currentTimeMillis().toInt())
+ it.mIsImpressed = false
+ /* ktlint-disable max-line-length */
+ logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+ it.mInstanceId,
+ it.mUid,
+ /* isRecommendationCard */ false,
+ intArrayOf(
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
+ rank = index,
+ receivedLatencyMillis = (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis).toInt())
+ /* ktlint-disable max-line-length */
+ }
+ }
+ }
addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
MediaPlayerData.getMediaPlayer(key)?.let {
+ /* ktlint-disable max-line-length */
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId,
it.mUid,
/* isRecommendationCard */ true,
- it.surfaceForSmartspaceLogging,
- rank = MediaPlayerData.getMediaPlayerIndex(key))
-
- if (mediaCarouselScrollHandler.visibleToUser &&
- mediaCarouselScrollHandler.visibleMediaIndex ==
- MediaPlayerData.getMediaPlayerIndex(key)) {
- logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN
- it.mInstanceId,
- it.mUid,
- /* isRecommendationCard */ true,
- it.surfaceForSmartspaceLogging)
- }
+ intArrayOf(
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
+ rank = MediaPlayerData.getMediaPlayerIndex(key),
+ receivedLatencyMillis = (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis).toInt())
+ /* ktlint-disable max-line-length */
+ }
+ if (mediaCarouselScrollHandler.visibleToUser &&
+ mediaCarouselScrollHandler.visibleMediaIndex
+ == MediaPlayerData.getMediaPlayerIndex(key)) {
+ logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
}
} else {
onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
@@ -400,7 +438,7 @@
}
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
- mediaCarousel.requiresRemeasuring = true
+ mediaFrame.requiresRemeasuring = true
// Check postcondition: mediaContent should have the same number of children as there are
// elements in mediaPlayers.
if (MediaPlayerData.players().size != mediaContent.childCount) {
@@ -439,7 +477,7 @@
updatePlayerToState(newRecs, noAnimation = true)
reorderAllPlayers(curVisibleMediaKey)
updatePageIndicator()
- mediaCarousel.requiresRemeasuring = true
+ mediaFrame.requiresRemeasuring = true
// Check postcondition: mediaContent should have the same number of children as there are
// elements in mediaPlayers.
if (MediaPlayerData.players().size != mediaContent.childCount) {
@@ -718,7 +756,8 @@
mediaControlPanel.mInstanceId,
mediaControlPanel.mUid,
isRecommendationCard,
- mediaControlPanel.surfaceForSmartspaceLogging)
+ intArrayOf(mediaControlPanel.surfaceForSmartspaceLogging))
+ mediaControlPanel.mIsImpressed = true
}
}
@@ -731,12 +770,15 @@
* instanceId
* @param uid uid for the application that media comes from
* @param isRecommendationCard whether the card is media recommendation
- * @param surface which display surface the media card is on (e.g. lockscreen, shade)
+ * @param surfaces list of display surfaces the media card is on (e.g. lockscreen, shade) when
+ * the event happened
* @param interactedSubcardRank the rank for interacted media item for recommendation card, -1
* for tapping on card but not on any media item, 0 for first media item, 1 for second, etc.
* @param interactedSubcardCardinality how many media items were shown to the user when there
* is user interaction
* @param rank the rank for media card in the media carousel, starting from 0
+ * @param receivedLatencyMillis latency in milliseconds for card received events. E.g. latency
+ * between headphone connection to sysUI displays media recommendation card
*
*/
fun logSmartspaceCardReported(
@@ -744,10 +786,11 @@
instanceId: Int,
uid: Int,
isRecommendationCard: Boolean,
- surface: Int,
+ surfaces: IntArray,
interactedSubcardRank: Int = 0,
interactedSubcardCardinality: Int = 0,
- rank: Int = mediaCarouselScrollHandler.visibleMediaIndex
+ rank: Int = mediaCarouselScrollHandler.visibleMediaIndex,
+ receivedLatencyMillis: Int = 0
) {
// Only log media resume card when Smartspace data is available
if (!isRecommendationCard &&
@@ -756,50 +799,52 @@
return
}
- /* ktlint-disable max-line-length */
- SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
- eventId,
- instanceId,
- // Deprecated, replaced with AiAi feature type so we don't need to create logging
- // card type for each new feature.
- SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
- surface,
- rank,
- mediaContent.getChildCount(),
- if (isRecommendationCard)
- 15 // MEDIA_RECOMMENDATION
- else
- 31, // MEDIA_RESUME
- uid,
- interactedSubcardRank,
- interactedSubcardCardinality,
- 0 // received_latency_millis
- )
- /* ktlint-disable max-line-length */
+ val cardinality = mediaContent.getChildCount()
+ surfaces.forEach { surface ->
+ /* ktlint-disable max-line-length */
+ SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
+ eventId,
+ instanceId,
+ // Deprecated, replaced with AiAi feature type so we don't need to create logging
+ // card type for each new feature.
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
+ surface,
+ rank,
+ cardinality,
+ if (isRecommendationCard)
+ 15 // MEDIA_RECOMMENDATION
+ else
+ 31, // MEDIA_RESUME
+ uid,
+ interactedSubcardRank,
+ interactedSubcardCardinality,
+ receivedLatencyMillis
+ )
+ /* ktlint-disable max-line-length */
+ if (DEBUG) {
+ Log.d(TAG, "Log Smartspace card event id: $eventId instance id: $instanceId" +
+ " surface: $surface rank: $rank cardinality: $cardinality " +
+ "isRecommendationCard: $isRecommendationCard uid: $uid " +
+ "interactedSubcardRank: $interactedSubcardRank " +
+ "interactedSubcardCardinality: $interactedSubcardCardinality " +
+ "received_latency_millis: $receivedLatencyMillis")
+ }
+ }
}
private fun onSwipeToDismiss() {
- val recommendation = MediaPlayerData.players().filter {
- it.recommendationViewHolder != null
- }
- // Use -1 as rank value to indicate user swipe to dismiss the card
- if (!recommendation.isEmpty()) {
- logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
- recommendation.get(0).mInstanceId,
- recommendation.get(0).mUid,
- true,
- recommendation.get(0).surfaceForSmartspaceLogging,
- rank = -1)
- } else {
- val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
- if (MediaPlayerData.players().size > visibleMediaIndex) {
- val player = MediaPlayerData.players().elementAt(visibleMediaIndex)
+ MediaPlayerData.players().forEachIndexed {
+ index, it ->
+ if (it.mIsImpressed) {
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
- player.mInstanceId,
- player.mUid,
- false,
- player.surfaceForSmartspaceLogging,
+ it.mInstanceId,
+ it.mUid,
+ it.recommendationViewHolder != null,
+ intArrayOf(it.surfaceForSmartspaceLogging),
+ // Use -1 as rank value to indicate user swipe to dismiss the card
rank = -1)
+ // Reset card impressed state when swipe to dismissed
+ it.mIsImpressed = false
}
}
mediaManager.onSwipeToDismiss()
@@ -833,12 +878,15 @@
)
private val comparator =
- compareByDescending<MediaSortKey> { it.data.isPlaying == true && it.data.isLocalSession }
- .thenByDescending { it.data.isPlaying }
- .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
- .thenByDescending { !it.data.resumption }
- .thenByDescending { it.updateTime }
- .thenByDescending { !it.data.isLocalSession }
+ compareByDescending<MediaSortKey> { it.data.isPlaying == true &&
+ it.data.playbackLocation == MediaData.PLAYBACK_LOCAL }
+ .thenByDescending { it.data.isPlaying == true &&
+ it.data.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL }
+ .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
+ .thenByDescending { !it.data.resumption }
+ .thenByDescending { it.data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE }
+ .thenByDescending { it.updateTime }
+ .thenByDescending { it.data.notificationKey }
private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator)
private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index e73eb66..f66eb5b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -59,6 +59,7 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.util.animation.TransitionLayout;
+import com.android.systemui.util.time.SystemClock;
import java.net.URISyntaxException;
import java.util.List;
@@ -121,6 +122,9 @@
private MediaCarouselController mMediaCarouselController;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
private final FalsingManager mFalsingManager;
+ // Used for swipe-to-dismiss logging.
+ protected boolean mIsImpressed = false;
+ private SystemClock mSystemClock;
/**
* Initialize a new control panel
@@ -134,7 +138,7 @@
SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory
mediaOutputDialogFactory, MediaCarouselController mediaCarouselController,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager, SystemClock systemClock) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
mActivityStarter = activityStarter;
@@ -145,6 +149,8 @@
mMediaOutputDialogFactory = mediaOutputDialogFactory;
mMediaCarouselController = mediaCarouselController;
mFalsingManager = falsingManager;
+ mSystemClock = systemClock;
+
loadDimens();
mSeekBarViewModel.setLogSmartspaceClick(() -> {
@@ -291,7 +297,10 @@
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Unable to look up package name", e);
}
- mInstanceId = SmallHash.hash(mUid);
+ // Only assigns instance id if it's unassigned.
+ if (mInstanceId == -1) {
+ mInstanceId = SmallHash.hash(mUid + (int) mSystemClock.currentTimeMillis());
+ }
mBackgroundColor = data.getBackgroundColor();
if (mToken == null || !mToken.equals(token)) {
@@ -885,7 +894,7 @@
mInstanceId,
mUid,
isRecommendationCard,
- getSurfaceForSmartspaceLogging(),
+ new int[]{getSurfaceForSmartspaceLogging()},
interactedSubcardRank,
interactedSubcardCardinality);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 6f0c887..bda07f4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -82,9 +82,9 @@
*/
var resumeAction: Runnable?,
/**
- * Local or remote playback
+ * Playback location: one of PLAYBACK_LOCAL, PLAYBACK_CAST_LOCAL, or PLAYBACK_CAST_REMOTE
*/
- var isLocalSession: Boolean = true,
+ var playbackLocation: Int = PLAYBACK_LOCAL,
/**
* Indicates that this player is a resumption player (ie. It only shows a play actions which
* will start the app and start playing).
@@ -110,7 +110,20 @@
* Timestamp when this player was last active.
*/
var lastActive: Long = 0L
-)
+) {
+ companion object {
+ /** Media is playing on the local device */
+ const val PLAYBACK_LOCAL = 0
+ /** Media is cast but originated on the local device */
+ const val PLAYBACK_CAST_LOCAL = 1
+ /** Media is from a remote cast notification */
+ const val PLAYBACK_CAST_REMOTE = 2
+ }
+
+ fun isLocalSession(): Boolean {
+ return playbackLocation == PLAYBACK_LOCAL
+ }
+}
/** State of a media action. */
data class MediaAction(
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index 296bfda..b68f2a7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -32,7 +32,7 @@
oldKey: String?,
data: MediaData,
immediately: Boolean,
- isSsReactivated: Boolean
+ receivedSmartspaceCardLatency: Int
) {
if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
entries[key] = data to entries.remove(oldKey)?.second
@@ -46,7 +46,8 @@
override fun onSmartspaceMediaDataLoaded(
key: String,
data: SmartspaceMediaData,
- shouldPrioritize: Boolean
+ shouldPrioritize: Boolean,
+ isSsReactivated: Boolean
) {
listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 79206e8..ae5c1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -56,7 +56,6 @@
class MediaDataFilter @Inject constructor(
private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
- private val mediaResumeListener: MediaResumeListener,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@Main private val executor: Executor,
private val systemClock: SystemClock
@@ -88,7 +87,7 @@
oldKey: String?,
data: MediaData,
immediately: Boolean,
- isSsReactivated: Boolean
+ receivedSmartspaceCardLatency: Int
) {
if (oldKey != null && oldKey != key) {
allEntries.remove(oldKey)
@@ -106,14 +105,15 @@
// Notify listeners
listeners.forEach {
- it.onMediaDataLoaded(key, oldKey, data, isSsReactivated = isSsReactivated)
+ it.onMediaDataLoaded(key, oldKey, data)
}
}
override fun onSmartspaceMediaDataLoaded(
key: String,
data: SmartspaceMediaData,
- shouldPrioritize: Boolean
+ shouldPrioritize: Boolean,
+ isSsReactivated: Boolean
) {
if (!data.isActive) {
Log.d(TAG, "Inactive recommendation data. Skip triggering.")
@@ -123,8 +123,6 @@
// Override the pass-in value here, as the order of Smartspace card is only determined here.
var shouldPrioritizeMutable = false
smartspaceMediaData = data
- // Override the pass-in value here, as the Smartspace reactivation could only happen here.
- var isSsReactivated = false
// Before forwarding the smartspace target, first check if we have recently inactive media
val sorted = userEntries.toSortedMap(compareBy {
@@ -139,18 +137,25 @@
smartspaceMaxAgeMillis = TimeUnit.SECONDS.toMillis(smartspaceMaxAgeSeconds)
}
}
+
+ val activeMedia = userEntries.filter { (key, value) -> value.active }
+ var isSsReactivatedMutable = activeMedia.isEmpty() && userEntries.isNotEmpty()
+
if (timeSinceActive < smartspaceMaxAgeMillis) {
- val lastActiveKey = sorted.lastKey() // most recently active
- // Notify listeners to consider this media active
- Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
- reactivatedKey = lastActiveKey
- if (MediaPlayerData.firstActiveMediaIndex() == -1) {
- isSsReactivated = true
- }
- val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
- listeners.forEach {
- it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData,
- isSsReactivated = isSsReactivated)
+ // It could happen there are existing active media resume cards, then we don't need to
+ // reactivate.
+ if (isSsReactivatedMutable) {
+ val lastActiveKey = sorted.lastKey() // most recently active
+ // Notify listeners to consider this media active
+ Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
+ reactivatedKey = lastActiveKey
+ val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
+ listeners.forEach {
+ it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData,
+ receivedSmartspaceCardLatency =
+ (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
+ .toInt())
+ }
}
} else {
// Mark to prioritize Smartspace card if no recent media.
@@ -161,7 +166,8 @@
Log.d(TAG, "Invalid recommendation data. Skip showing the rec card")
return
}
- listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
+ listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable,
+ isSsReactivatedMutable) }
}
override fun onMediaDataRemoved(key: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index c9b6d9c..7c0f7fc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -27,6 +27,8 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.ImageDecoder
@@ -82,7 +84,7 @@
emptyList(), emptyList(), "INVALID", null, null, null, true, null)
@VisibleForTesting
internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false,
- "INVALID", null, emptyList(), null, 0)
+ "INVALID", null, emptyList(), null, 0, 0)
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
return sbn.notification.isMediaNotification()
@@ -145,6 +147,24 @@
private var smartspaceSession: SmartspaceSession? = null
private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)
+ /**
+ * Check whether this notification is an RCN
+ * TODO(b/204910409) implement new API for explicitly declaring this
+ */
+ private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
+ val pm = context.packageManager
+ try {
+ val info = pm.getApplicationInfo(sbn.packageName, PackageManager.MATCH_SYSTEM_ONLY)
+ if (info.privateFlags and ApplicationInfo.PRIVATE_FLAG_PRIVILEGED != 0) {
+ val extras = sbn.notification.extras
+ if (extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
+ return true
+ }
+ }
+ } catch (e: PackageManager.NameNotFoundException) { }
+ return false
+ }
+
@Inject
constructor(
context: Context,
@@ -434,7 +454,7 @@
val existed = mediaEntries[key] != null
backgroundExecutor.execute {
mediaEntries[key]?.let { mediaData ->
- if (mediaData.isLocalSession) {
+ if (mediaData.isLocalSession()) {
mediaData.token?.let {
val mediaController = mediaControllerFactory.create(it)
mediaController.transportControls.stop()
@@ -618,8 +638,11 @@
}
}
- val isLocalSession = mediaController.playbackInfo?.playbackType ==
- MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL
+ val playbackLocation =
+ if (isRemoteCastNotification(sbn)) MediaData.PLAYBACK_CAST_REMOTE
+ else if (mediaController.playbackInfo?.playbackType ==
+ MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) MediaData.PLAYBACK_LOCAL
+ else MediaData.PLAYBACK_CAST_LOCAL
val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
val lastActive = systemClock.elapsedRealtime()
foregroundExecutor.execute {
@@ -629,7 +652,7 @@
onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
smallIcon, artist, song, artWorkIcon, actionIcons,
actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
- active, resumeAction = resumeAction, isLocalSession = isLocalSession,
+ active, resumeAction = resumeAction, playbackLocation = playbackLocation,
notificationKey = key, hasCheckedForResume = hasCheckedForResume,
isPlaying = isPlaying, isClearable = sbn.isClearable(),
lastActive = lastActive))
@@ -754,7 +777,7 @@
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
val removed = mediaEntries.remove(key)
- if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession) {
+ if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession()) {
Log.d(TAG, "Not removing $key because resumable")
// Move to resume key (aka package name) if that key doesn't already exist.
val resumeAction = getResumeMediaAction(removed.resumeAction!!)
@@ -829,15 +852,16 @@
* until the next refresh-round before UI becomes visible. True by default to take in place
* immediately.
*
- * @param isSsReactivated indicates transition from a state with no active media players to
- * a state with active media players upon receiving Smartspace media data.
+ * @param receivedSmartspaceCardLatency is the latency between headphone connects and sysUI
+ * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace
+ * signal.
*/
fun onMediaDataLoaded(
key: String,
oldKey: String?,
data: MediaData,
immediately: Boolean = true,
- isSsReactivated: Boolean = false
+ receivedSmartspaceCardLatency: Int = 0
) {}
/**
@@ -846,11 +870,15 @@
* @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true,
* it will be prioritized as the first card. Otherwise, it will show up as the last card as
* default.
+ *
+ * @param isSsReactivated indicates resume media card is reactivated by Smartspace
+ * recommendation signal
*/
fun onSmartspaceMediaDataLoaded(
key: String,
data: SmartspaceMediaData,
- shouldPrioritize: Boolean = false
+ shouldPrioritize: Boolean = false,
+ isSsReactivated: Boolean = false
) {}
/** Called whenever a previously existing Media notification was removed. */
@@ -886,12 +914,13 @@
packageName(target)?.let {
return SmartspaceMediaData(target.smartspaceTargetId, isActive, true, it,
target.baseAction, target.iconGrid,
- dismissIntent, 0)
+ dismissIntent, 0, target.creationTimeMillis)
}
return EMPTY_SMARTSPACE_MEDIA_DATA
.copy(targetId = target.smartspaceTargetId,
- isActive = isActive,
- dismissIntent = dismissIntent)
+ isActive = isActive,
+ dismissIntent = dismissIntent,
+ headphoneConnectionTimeMillis = target.creationTimeMillis)
}
private fun packageName(target: SmartspaceTarget): String? {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index fbfb919..bed254f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -68,7 +68,7 @@
oldKey: String?,
data: MediaData,
immediately: Boolean,
- isSsReactivated: Boolean
+ receivedSmartspaceCardLatency: Int
) {
if (oldKey != null && oldKey != key) {
val oldEntry = entries.remove(oldKey)
@@ -200,7 +200,7 @@
@WorkerThread
private fun updateCurrent() {
val device = localMediaManager.currentConnectedDevice
- val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it)}
+ val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
// If we have a controller but get a null route, then don't trust the device
val enabled = device != null && (controller == null || route != null)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index fb601e3..c8cd432 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -21,6 +21,7 @@
import android.animation.ValueAnimator
import android.annotation.IntDef
import android.content.Context
+import android.content.res.Configuration
import android.graphics.Rect
import android.util.MathUtils
import android.view.View
@@ -41,6 +42,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.Utils
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
@@ -186,6 +188,8 @@
@MediaLocation
private var currentAttachmentLocation = -1
+ private var inSplitShade = false
+
/**
* Is there any active media in the carousel?
*/
@@ -390,8 +394,9 @@
init {
updateConfiguration()
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
- override fun onDensityOrFontScaleChanged() {
+ override fun onConfigChanged(newConfig: Configuration?) {
updateConfiguration()
+ updateDesiredLocation(forceNoAnimation = true, forceStateUpdate = true)
}
})
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
@@ -467,6 +472,7 @@
private fun updateConfiguration() {
distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_media_transition_distance)
+ inSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
}
/**
@@ -803,7 +809,7 @@
private fun getQSTransformationProgress(): Float {
val currentHost = getHost(desiredLocation)
val previousHost = getHost(previousLocation)
- if (hasActiveMedia && currentHost?.location == LOCATION_QS) {
+ if (hasActiveMedia && (currentHost?.location == LOCATION_QS && !inSplitShade)) {
if (previousHost?.location == LOCATION_QQS) {
if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) {
return qsExpansion
@@ -934,7 +940,7 @@
statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
val location = when {
- qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
+ (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index ff085c36..0a4b68b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -61,7 +61,7 @@
oldKey: String?,
data: MediaData,
immediately: Boolean,
- isSsReactivated: Boolean
+ receivedSmartspaceCardLatency: Int
) {
if (immediately) {
updateViewVisibility()
@@ -71,7 +71,8 @@
override fun onSmartspaceMediaDataLoaded(
key: String,
data: SmartspaceMediaData,
- shouldPrioritize: Boolean
+ shouldPrioritize: Boolean,
+ isSsReactivated: Boolean
) {
updateViewVisibility()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 608c784..35f95dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -184,7 +184,7 @@
oldKey: String?,
data: MediaData,
immediately: Boolean,
- isSsReactivated: Boolean
+ receivedSmartspaceCardLatency: Int
) {
if (useMediaResumption) {
// If this had been started from a resume state, disconnect now that it's live
@@ -193,7 +193,7 @@
mediaBrowser = null
}
// If we don't have a resume action, check if we haven't already
- if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession) {
+ if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession()) {
// TODO also check for a media button receiver intended for restarting (b/154127084)
Log.d(TAG, "Checking for service component for " + data.packageName)
val pm = context.packageManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
index 8bddde8..1c448a2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
@@ -96,7 +96,7 @@
oldKey: String?,
data: MediaData,
immediately: Boolean,
- isSsReactivated: Boolean
+ receivedSmartspaceCardLatency: Int
) {
backgroundExecutor.execute {
data.token?.let {
@@ -143,7 +143,8 @@
override fun onSmartspaceMediaDataLoaded(
key: String,
data: SmartspaceMediaData,
- shouldPrioritize: Boolean
+ shouldPrioritize: Boolean,
+ isSsReactivated: Boolean
) {
backgroundExecutor.execute {
dispatchSmartspaceMediaDataLoaded(key, data)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 6f04771..9581a63 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -63,7 +63,7 @@
oldKey: String?,
data: MediaData,
immediately: Boolean,
- isSsReactivated: Boolean
+ receivedSmartspaceCardLatency: Int
) {
var reusedListener: PlaybackStateListener? = null
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
index 61fdefd..066a6fb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
@@ -52,5 +52,9 @@
/**
* View's background color.
*/
- val backgroundColor: Int
+ val backgroundColor: Int,
+ /**
+ * The timestamp in milliseconds that headphone is connected.
+ */
+ val headphoneConnectionTimeMillis: Long
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 868193b..54e40f1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -289,6 +289,9 @@
public void onAnimationEnd(Animator animation) {
to.requireViewById(R.id.volume_indeterminate_progress).setVisibility(
View.VISIBLE);
+ // Unset the listener, otherwise this may persist for another view
+ // property animation
+ toTitleText.animate().setListener(null);
}
});
// Animation for seek bar
@@ -312,8 +315,14 @@
public void onAnimationEnd(Animator animation) {
mIsAnimating = false;
notifyDataSetChanged();
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ fromTitleText.animate().setListener(null);
}
});
+ // Unset the listener, otherwise this may persist for another view
+ // property animation
+ fromSeekBar.animate().setListener(null);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 26ce645..f2cb254 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -44,6 +44,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Base dialog for media output UI
@@ -52,6 +53,7 @@
MediaOutputController.Callback, Window.Callback {
private static final String TAG = "MediaOutputDialog";
+ private static final String EMPTY_TITLE = " ";
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final RecyclerView.LayoutManager mLayoutManager;
@@ -63,13 +65,14 @@
View mDialogView;
private TextView mHeaderTitle;
private TextView mHeaderSubtitle;
- private ImageView mHeaderIcon;
private RecyclerView mDevicesRecyclerView;
private LinearLayout mDeviceListLayout;
private Button mDoneButton;
private Button mStopButton;
private int mListMaxHeight;
+ protected ImageView mHeaderIcon;
+
MediaOutputBaseAdapter mAdapter;
private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
@@ -81,9 +84,12 @@
}
};
- public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
- super(context);
- mContext = context;
+ public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController,
+ SystemUIDialogManager dialogManager) {
+ super(context, dialogManager);
+
+ // Save the context that is wrapped with our theme.
+ mContext = getContext();
mMediaOutputController = mediaOutputController;
mLayoutManager = new LinearLayoutManager(mContext);
mListMaxHeight = context.getResources().getDimensionPixelSize(
@@ -104,6 +110,9 @@
lp.setFitInsetsIgnoringVisibility(true);
window.setAttributes(lp);
window.setContentView(mDialogView);
+ // Sets window to a blank string to avoid talkback announce app label first when pop up,
+ // which doesn't make sense.
+ window.setTitle(EMPTY_TITLE);
mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
@@ -140,7 +149,6 @@
mMediaOutputController.stop();
}
- @VisibleForTesting
void refresh() {
// Update header icon
final int iconRes = getHeaderIconRes();
@@ -154,12 +162,6 @@
} else {
mHeaderIcon.setVisibility(View.GONE);
}
- if (mHeaderIcon.getVisibility() == View.VISIBLE) {
- final int size = getHeaderIconSize();
- final int padding = mContext.getResources().getDimensionPixelSize(
- R.dimen.media_output_dialog_header_icon_padding);
- mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
- }
// Update title and subtitle
mHeaderTitle.setText(getHeaderText());
final CharSequence subTitle = getHeaderSubtitle();
@@ -217,14 +219,6 @@
dismiss();
}
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- if (!hasFocus && isShowing()) {
- dismiss();
- }
- }
-
void onHeaderIconClick() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 6da4d48..a1e2c57 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -16,6 +16,8 @@
package com.android.systemui.media.dialog;
+import static android.provider.Settings.ACTION_BLUETOOTH_PAIRING_SETTINGS;
+
import android.app.Notification;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +32,7 @@
import android.media.session.PlaybackState;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -46,7 +49,6 @@
import com.android.settingslib.media.InfoMediaManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.media.MediaOutputConstants;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.animation.DialogLaunchAnimator;
@@ -54,6 +56,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import java.util.ArrayList;
import java.util.Collection;
@@ -69,7 +72,8 @@
private static final String TAG = "MediaOutputController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
+ private static final String PAGE_CONNECTED_DEVICES_KEY =
+ "top_level_connected_devices";
private final String mPackageName;
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
@@ -77,6 +81,7 @@
private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter;
private final DialogLaunchAnimator mDialogLaunchAnimator;
+ private final SystemUIDialogManager mDialogManager;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
private final boolean mVolumeAdjustmentForRemoteGroupSessions;
@@ -98,7 +103,7 @@
boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
lbm, ShadeController shadeController, ActivityStarter starter,
NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ DialogLaunchAnimator dialogLaunchAnimator, SystemUIDialogManager dialogManager) {
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
@@ -114,6 +119,7 @@
mDialogLaunchAnimator = dialogLaunchAnimator;
mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
+ mDialogManager = dialogManager;
}
void start(@NonNull Callback cb) {
@@ -451,23 +457,34 @@
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mCallback.dismissDialog();
- final ActivityStarter.OnDismissAction postKeyguardAction = () -> {
- mContext.sendBroadcast(new Intent()
- .setAction(MediaOutputConstants.ACTION_LAUNCH_BLUETOOTH_PAIRING)
- .setPackage(MediaOutputConstants.SETTINGS_PACKAGE_NAME));
- mShadeController.animateCollapsePanels();
- return true;
- };
- mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
+ Intent launchIntent =
+ new Intent(ACTION_BLUETOOTH_PAIRING_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ final Intent deepLinkIntent =
+ new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
+ if (deepLinkIntent.resolveActivity(mContext.getPackageManager()) != null) {
+ Log.d(TAG, "Device support split mode, launch page with deep link");
+ deepLinkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ deepLinkIntent.putExtra(
+ Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
+ launchIntent.toUri(Intent.URI_INTENT_SCHEME));
+ deepLinkIntent.putExtra(
+ Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
+ PAGE_CONNECTED_DEVICES_KEY);
+ mActivityStarter.startActivity(deepLinkIntent, true);
+ return;
+ }
+ mActivityStarter.startActivity(launchIntent, true);
}
void launchMediaOutputGroupDialog(View mediaOutputDialog) {
// We show the output group dialog from the output dialog.
MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController,
- mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator,
+ mDialogManager);
MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar,
- controller);
+ controller, mDialogManager);
mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index eca8ac9..7e2558c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -28,6 +28,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Dialog for media output transferring.
@@ -37,8 +38,9 @@
final UiEventLogger mUiEventLogger;
MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
- mediaOutputController, UiEventLogger uiEventLogger) {
- super(context, mediaOutputController);
+ mediaOutputController, UiEventLogger uiEventLogger,
+ SystemUIDialogManager dialogManager) {
+ super(context, mediaOutputController, dialogManager);
mUiEventLogger = uiEventLogger;
mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index b91901d..a7bc852 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -25,6 +25,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
import javax.inject.Inject
/**
@@ -38,7 +39,8 @@
private val starter: ActivityStarter,
private val notificationEntryManager: NotificationEntryManager,
private val uiEventLogger: UiEventLogger,
- private val dialogLaunchAnimator: DialogLaunchAnimator
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val dialogManager: SystemUIDialogManager
) {
companion object {
var mediaOutputDialog: MediaOutputDialog? = null
@@ -51,8 +53,9 @@
val controller = MediaOutputController(context, packageName, aboveStatusBar,
mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
- uiEventLogger, dialogLaunchAnimator)
- val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
+ uiEventLogger, dialogLaunchAnimator, dialogManager)
+ val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger,
+ dialogManager)
mediaOutputDialog = dialog
// Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index 1300400..478b890 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -20,10 +20,12 @@
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
+import android.widget.LinearLayout;
import androidx.core.graphics.drawable.IconCompat;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Dialog for media output group.
@@ -31,8 +33,8 @@
public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
- mediaOutputController) {
- super(context, mediaOutputController);
+ mediaOutputController, SystemUIDialogManager dialogManager) {
+ super(context, mediaOutputController, dialogManager);
mMediaOutputController.resetGroupMediaDevices();
mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
if (!aboveStatusbar) {
@@ -76,6 +78,15 @@
}
@Override
+ void refresh() {
+ super.refresh();
+ final int size = getHeaderIconSize();
+ final int padding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_header_icon_padding);
+ mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
+ }
+
+ @Override
int getStopButtonVisibility() {
return View.VISIBLE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
new file mode 100644
index 0000000..42b7cc3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -0,0 +1,272 @@
+/*
+ * 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.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.inputmethodservice.InputMethodService;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/**
+ * Extracts shared elements between navbar and taskbar delegate to de-dupe logic and help them
+ * experience the joys of friendship.
+ * The events are then passed through
+ *
+ * Currently consolidates
+ * * A11y
+ * * Assistant
+ */
+@SysUISingleton
+public final class NavBarHelper implements
+ AccessibilityButtonModeObserver.ModeChangedListener,
+ OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ Dumpable {
+ private final AccessibilityManager mAccessibilityManager;
+ private final Lazy<AssistManager> mAssistManagerLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final UserTracker mUserTracker;
+ private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
+ private final Context mContext;
+ private ContentResolver mContentResolver;
+ private boolean mAssistantAvailable;
+ private boolean mLongPressHomeEnabled;
+ private boolean mAssistantTouchGestureEnabled;
+ private int mNavBarMode;
+
+ private final ContentObserver mAssistContentObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateAssitantAvailability();
+ }
+ };
+
+ /**
+ * @param context This is not display specific, then again neither is any of the code in
+ * this class. Once there's display specific code, we may want to create an
+ * instance of this class per navbar vs having it be a singleton.
+ */
+ @Inject
+ public NavBarHelper(Context context, AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+ OverviewProxyService overviewProxyService,
+ Lazy<AssistManager> assistManagerLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ NavigationModeController navigationModeController,
+ UserTracker userTracker,
+ DumpManager dumpManager) {
+ mContext = context;
+ mContentResolver = mContext.getContentResolver();
+ mAccessibilityManager = accessibilityManager;
+ mAssistManagerLazy = assistManagerLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mUserTracker = userTracker;
+ accessibilityManagerWrapper.addCallback(
+ accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate());
+ mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+
+ mAccessibilityButtonModeObserver.addListener(this);
+ mNavBarMode = navigationModeController.addListener(this);
+ overviewProxyService.addCallback(this);
+ dumpManager.registerDumpable(this);
+ }
+
+ public void init() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
+ false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ updateAssitantAvailability();
+ }
+
+ public void destroy() {
+ mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ }
+
+ /**
+ * @param listener Will immediately get callbacks based on current state
+ */
+ public void registerNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.add(listener);
+ listener.updateAccessibilityServicesState();
+ listener.updateAssistantAvailable(mAssistantAvailable);
+ }
+
+ public void removeNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.remove(listener);
+ }
+
+ private void dispatchA11yEventUpdate() {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAccessibilityServicesState();
+ }
+ }
+
+ private void dispatchAssistantEventUpdate(boolean assistantAvailable) {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAssistantAvailable(assistantAvailable);
+ }
+ }
+
+ @Override
+ public void onAccessibilityButtonModeChanged(int mode) {
+ dispatchA11yEventUpdate();
+ }
+
+ /**
+ * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ *
+ * @return the a11y button clickable and long_clickable states, or 0 if there is no
+ * a11y button in the navbar
+ */
+ public int getA11yButtonState() {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return 0;
+ }
+
+ return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateAssitantAvailability();
+ }
+ }
+
+ private void updateAssitantAvailability() {
+ boolean assistantAvailableForUser = mAssistManagerLazy.get()
+ .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ boolean longPressDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
+ mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+ boolean gestureDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
+ mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+
+ mAssistantAvailable = assistantAvailableForUser
+ && mAssistantTouchGestureEnabled
+ && QuickStepContract.isGesturalMode(mNavBarMode);
+ dispatchAssistantEventUpdate(mAssistantAvailable);
+ }
+
+ public boolean getLongPressHomeEnabled() {
+ return mLongPressHomeEnabled;
+ }
+
+ @Override
+ public void startAssistant(Bundle bundle) {
+ mAssistManagerLazy.get().startAssist(bundle);
+ }
+
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ updateAssitantAvailability();
+ }
+
+ /**
+ * @return Whether the IME is shown on top of the screen given the {@code vis} flag of
+ * {@link InputMethodService} and the keyguard states.
+ */
+ public boolean isImeShown(int vis) {
+ View shadeWindowView = mStatusBarOptionalLazy.get().get().getNotificationShadeWindowView();
+ boolean isKeyguardShowing = mStatusBarOptionalLazy.get().get().isKeyguardShowing();
+ boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
+ && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+ return imeVisibleOnShade
+ || (!isKeyguardShowing && (vis & InputMethodService.IME_VISIBLE) != 0);
+ }
+
+ /**
+ * Callbacks will get fired once immediately after registering via
+ * {@link #registerNavTaskStateUpdater(NavbarTaskbarStateUpdater)}
+ */
+ public interface NavbarTaskbarStateUpdater {
+ void updateAccessibilityServicesState();
+ void updateAssistantAvailable(boolean available);
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("NavbarTaskbarFriendster");
+ pw.println(" longPressHomeEnabled=" + mLongPressHomeEnabled);
+ pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
+ pw.println(" mAssistantAvailable=" + mAssistantAvailable);
+ pw.println(" mNavBarMode=" + mNavBarMode);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index bc023cc..963576b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -68,22 +68,17 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.database.ContentObserver;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.inputmethodservice.InputMethodService;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
@@ -200,8 +195,7 @@
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
- private final UserTracker mUserTracker;
+ private final NavBarHelper mNavBarHelper;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private Bundle mSavedState;
@@ -213,9 +207,7 @@
private int mNavigationIconHints = 0;
private @TransitionMode int mNavigationBarMode;
private ContentResolver mContentResolver;
- private boolean mAssistantAvailable;
private boolean mLongPressHomeEnabled;
- private boolean mAssistantTouchGestureEnabled;
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -309,16 +301,31 @@
}
};
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateAcessibilityStateFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ // TODO(b/198002034): Content observers currently can still be called back after
+ // being unregistered, and in this case we can ignore the change if the nav bar
+ // has been destroyed already
+ if (mNavigationBarView == null) {
+ return;
+ }
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
+ updateAssistantEntrypoints(available);
+ }
+ };
+
private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
mNavigationBarView.updateStates();
updateScreenPinningGestures();
-
- // Send the assistant availability upon connection
- if (isConnected) {
- updateAssistantEntrypoints();
- }
}
@Override
@@ -421,20 +428,6 @@
}
};
- private final ContentObserver mAssistContentObserver = new ContentObserver(
- new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- // TODO(b/198002034): Content observers currently can still be called back after being
- // unregistered, and in this case we can ignore the change if the nav bar has been
- // destroyed already
- if (mNavigationBarView == null) {
- return;
- }
- updateAssistantEntrypoints();
- }
- };
-
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -504,7 +497,7 @@
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -535,8 +528,7 @@
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
- mUserTracker = userTracker;
+ mNavBarHelper = navBarHelper;
mNotificationShadeDepthController = notificationShadeDepthController;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -568,18 +560,9 @@
mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mCommandQueue.addCallback(this);
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mContentResolver = mContext.getContentResolver();
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
- false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
+ mNavBarHelper.init();
mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
R.bool.allow_force_nav_bar_handle_opaque);
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
@@ -593,7 +576,6 @@
)).filter(duration -> duration != 0);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
- updateAssistantEntrypoints();
if (savedState != null) {
mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -620,8 +602,8 @@
mWindowManager.removeViewImmediate(mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
- mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
@@ -643,10 +625,10 @@
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
- mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
+ mPipOptional.ifPresent(mNavigationBarView::addPipExclusionBoundsChangeListener);
prepareNavigationBarView();
checkNavBarModes();
@@ -716,7 +698,8 @@
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mPipOptional.ifPresent(mNavigationBarView::removePipExclusionBoundsChangeListener);
mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
@@ -885,7 +868,6 @@
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
- pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
pw.println(" mNavigationBarWindowState="
+ windowStateToString(mNavigationBarWindowState));
pw.println(" mNavigationBarMode="
@@ -902,7 +884,8 @@
if (displayId != mDisplayId) {
return;
}
- boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+ boolean imeShown = mNavBarHelper.isImeShown(vis);
+ showImeSwitcher = imeShown && showImeSwitcher;
int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
imeShown, showImeSwitcher);
if (hints == mNavigationIconHints) return;
@@ -1165,7 +1148,7 @@
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1270,6 +1253,7 @@
private void onImeSwitcherClick(View v) {
mInputMethodManager.showInputMethodPickerFromSystem(
true /* showAuxiliarySubtypes */, mDisplayId);
+ mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
};
private boolean onLongPressBackHome(View v) {
@@ -1390,8 +1374,8 @@
return true;
}
- void updateAccessibilityServicesState() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ void updateAcessibilityStateFlags() {
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
if (mNavigationBarView != null) {
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
@@ -1403,7 +1387,7 @@
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
- a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ a11yFlags = mNavBarHelper.getA11yButtonState();
}
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1430,24 +1414,10 @@
}
}
- private void updateAssistantEntrypoints() {
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
- boolean longPressDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
- mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
- boolean gestureDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
- mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
+ private void updateAssistantEntrypoints(boolean assistantAvailable) {
if (mOverviewProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
- && mAssistantTouchGestureEnabled
- && QuickStepContract.isGesturalMode(mNavBarMode));
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
}
@@ -1518,8 +1488,6 @@
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
- // update assistant entry points on system navigation radio button click
- updateAssistantEntrypoints();
if (!QuickStepContract.isGesturalMode(mode)) {
// Reset the override alpha
@@ -1558,9 +1526,6 @@
mNavigationBarView.getBarTransitions().finishAnimations();
}
- private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
- this::updateAccessibilityServicesState;
-
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
lp.paramsForRotation = new WindowManager.LayoutParams[4];
@@ -1668,7 +1633,7 @@
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
}
}
};
@@ -1704,7 +1669,7 @@
private final Handler mMainHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private final NavBarHelper mNavBarHelper;
private final UserTracker mUserTracker;
private final LightBarController mMainLightBarController;
private final LightBarController.Factory mLightBarControllerFactory;
@@ -1737,7 +1702,7 @@
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -1767,7 +1732,7 @@
mMainHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mUserTracker = userTracker;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -1788,10 +1753,10 @@
mSplitScreenOptional, mRecentsOptional, mStatusBarOptionalLazy,
mShadeController, mNotificationRemoteInputManager,
mNotificationShadeDepthController, mSystemActions, mMainHandler,
- mNavbarOverlayController, mUiEventLogger, mNavigationBarA11yHelper,
+ mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
mUserTracker, mMainLightBarController, mLightBarControllerFactory,
mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
mInputMethodManager);
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
deleted file mode 100644
index 13e6d8b..0000000
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.android.systemui.navigationbar;
-
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
-
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Extracts shared elements of a11y necessary between navbar and taskbar delegate
- */
-@SysUISingleton
-public final class NavigationBarA11yHelper implements
- AccessibilityButtonModeObserver.ModeChangedListener {
- private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
- private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
-
- @Inject
- public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
- mAccessibilityManager = accessibilityManager;
- accessibilityManagerWrapper.addCallback(
- accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
- mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
-
- mAccessibilityButtonModeObserver.addListener(this);
- }
-
- public void registerA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.add(listener);
- }
-
- public void removeA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.remove(listener);
- }
-
- private void dispatchEventUpdate() {
- for (NavA11yEventListener listener : mA11yEventListeners) {
- listener.updateAccessibilityServicesState();
- }
- }
-
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- dispatchEventUpdate();
- }
-
- /**
- * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
- * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
- *
- * @return the a11y button clickable and long_clickable states, or 0 if there is no
- * a11y button in the navbar
- */
- public int getA11yButtonState() {
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
-
- public interface NavA11yEventListener {
- void updateAccessibilityServicesState();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 3dc79c4..a984974 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,10 +57,13 @@
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.pip.Pip;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
import javax.inject.Inject;
@@ -100,11 +103,13 @@
CommandQueue commandQueue,
@Main Handler mainHandler,
ConfigurationController configurationController,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
TaskbarDelegate taskbarDelegate,
NavigationBar.Factory navigationBarFactory,
DumpManager dumpManager,
- AutoHideController autoHideController) {
+ AutoHideController autoHideController,
+ LightBarController lightBarController,
+ Optional<Pip> pipOptional) {
mContext = context;
mHandler = mainHandler;
mNavigationBarFactory = navigationBarFactory;
@@ -115,8 +120,8 @@
mNavMode = navigationModeController.addListener(this);
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
- navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer,
- dumpManager, autoHideController);
+ navBarHelper, navigationModeController, sysUiFlagsContainer,
+ dumpManager, autoHideController, lightBarController, pipOptional);
mIsTablet = isTablet(mContext);
dumpManager.registerDumpable(this);
}
@@ -393,6 +398,24 @@
return (navBar == null) ? null : navBar.getView();
}
+ public void showPinningEnterExitToast(int displayId, boolean entering) {
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ navBarView.showPinningEnterExitToast(entering);
+ } else if (displayId == DEFAULT_DISPLAY && mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.showPinningEnterExitToast(entering);
+ }
+ }
+
+ public void showPinningEscapeToast(int displayId) {
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ navBarView.showPinningEscapeToast();
+ } else if (displayId == DEFAULT_DISPLAY && mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.showPinningEscapeToast();
+ }
+ }
+
/** @return {@link NavigationBar} on the default display. */
@Nullable
public NavigationBar getDefaultNavigationBar() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 680cc5c..7adb7ac 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -75,12 +75,12 @@
import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
import com.android.systemui.navigationbar.buttons.RotationContextButton;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
+import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.shared.rotation.RotationButtonController;
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
@@ -110,6 +110,7 @@
private final int mNavColorSampleMargin;
private final SysUiState mSysUiFlagContainer;
+ // The current view is one of mHorizontal or mVertical depending on the current configuration
View mCurrentView = null;
private View mVertical;
private View mHorizontal;
@@ -397,12 +398,6 @@
}
}
- @Override
- protected boolean onSetAlpha(int alpha) {
- Log.e(TAG, "onSetAlpha", new Throwable());
- return super.onSetAlpha(alpha);
- }
-
public void setAutoHideController(AutoHideController autoHideController) {
mAutoHideController = autoHideController;
}
@@ -505,6 +500,18 @@
return mCurrentView;
}
+ /**
+ * Applies {@param consumer} to each of the nav bar views.
+ */
+ public void forEachView(Consumer<View> consumer) {
+ if (mVertical != null) {
+ consumer.accept(mVertical);
+ }
+ if (mHorizontal != null) {
+ consumer.accept(mHorizontal);
+ }
+ }
+
public RotationButtonController getRotationButtonController() {
return mRotationButtonController;
}
@@ -1205,7 +1212,9 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mTmpLastConfiguration.updateFrom(mConfiguration);
- mConfiguration.updateFrom(newConfig);
+ final int changes = mConfiguration.updateFrom(newConfig);
+ mFloatingRotationButton.onConfigurationChanged(changes);
+
boolean uiCarModeChanged = updateCarMode();
updateIcons(mTmpLastConfiguration);
updateRecentsIcon();
@@ -1377,8 +1386,12 @@
legacySplitScreen.registerInSplitScreenListener(mDockedListener);
}
- void registerPipExclusionBoundsChangeListener(Pip pip) {
- pip.setPipExclusionBoundsChangeListener(mPipListener);
+ void addPipExclusionBoundsChangeListener(Pip pip) {
+ pip.addPipExclusionBoundsChangeListener(mPipListener);
+ }
+
+ void removePipExclusionBoundsChangeListener(Pip pip) {
+ pip.removePipExclusionBoundsChangeListener(mPipListener);
}
private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index d707dbd..feda99f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -40,9 +40,12 @@
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.Display;
import android.view.InsetsVisibilities;
import android.view.View;
@@ -59,12 +62,19 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import com.android.wm.shell.pip.Pip;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -73,25 +83,43 @@
public class TaskbarDelegate implements CommandQueue.Callbacks,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
ComponentCallbacks, Dumpable {
+ private static final String TAG = TaskbarDelegate.class.getSimpleName();
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
-
+ private boolean mInitialized;
private CommandQueue mCommandQueue;
private OverviewProxyService mOverviewProxyService;
- private NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private NavBarHelper mNavBarHelper;
private NavigationModeController mNavigationModeController;
private SysUiState mSysUiState;
private AutoHideController mAutoHideController;
+ private LightBarController mLightBarController;
+ private LightBarTransitionsController mLightBarTransitionsController;
+ private Optional<Pip> mPipOptional;
private int mDisplayId;
private int mNavigationIconHints;
- private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
- this::updateSysuiFlags;
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateSysuiFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ updateAssistantAvailability(available);
+ }
+ };
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
private @Behavior int mBehavior;
private final Context mContext;
private final DisplayManager mDisplayManager;
private Context mWindowContext;
+ private ScreenPinningNotify mScreenPinningNotify;
+ private int mNavigationMode;
+ private final Consumer<Rect> mPipListener;
+
/**
* Tracks the system calls for when taskbar should transiently show or hide so we can return
* this value in {@link AutoHideUiElement#isVisible()} below.
@@ -121,56 +149,117 @@
.create(context);
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mPipListener = mEdgeBackGestureHandler::setPipStashExclusionBounds;
}
public void setDependencies(CommandQueue commandQueue,
OverviewProxyService overviewProxyService,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
NavigationModeController navigationModeController,
SysUiState sysUiState, DumpManager dumpManager,
- AutoHideController autoHideController) {
+ AutoHideController autoHideController,
+ LightBarController lightBarController,
+ Optional<Pip> pipOptional) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mNavigationModeController = navigationModeController;
mSysUiState = sysUiState;
dumpManager.registerDumpable(this);
mAutoHideController = autoHideController;
+ mLightBarController = lightBarController;
+ mLightBarTransitionsController = createLightBarTransitionsController();
+ mPipOptional = pipOptional;
+ }
+
+ // Separated into a method to keep setDependencies() clean/readable.
+ private LightBarTransitionsController createLightBarTransitionsController() {
+ return new LightBarTransitionsController(mContext,
+ new LightBarTransitionsController.DarkIntensityApplier() {
+ @Override
+ public void applyDarkIntensity(float darkIntensity) {
+ mOverviewProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
+ }
+
+ @Override
+ public int getTintAnimationDuration() {
+ return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
+ }
+ }, mCommandQueue) {
+ @Override
+ public boolean supportsIconTintForNavMode(int navigationMode) {
+ // Always tint taskbar nav buttons (region sampling handles gesture bar separately).
+ return true;
+ }
+ };
}
public void init(int displayId) {
+ if (mInitialized) {
+ return;
+ }
mDisplayId = displayId;
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(this);
mEdgeBackGestureHandler.onNavigationModeChanged(
mNavigationModeController.addListener(this));
- mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.init();
mEdgeBackGestureHandler.onNavBarAttached();
// Initialize component callback
Display display = mDisplayManager.getDisplay(displayId);
mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
mWindowContext.registerComponentCallbacks(this);
+ mScreenPinningNotify = new ScreenPinningNotify(mWindowContext);
// Set initial state for any listeners
updateSysuiFlags();
mAutoHideController.setNavigationBar(mAutoHideUiElement);
+ mLightBarController.setNavigationBar(mLightBarTransitionsController);
+ mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
+ mInitialized = true;
}
public void destroy() {
+ if (!mInitialized) {
+ return;
+ }
mCommandQueue.removeCallback(this);
mOverviewProxyService.removeCallback(this);
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mEdgeBackGestureHandler.onNavBarDetached();
+ mScreenPinningNotify = null;
if (mWindowContext != null) {
mWindowContext.unregisterComponentCallbacks(this);
mWindowContext = null;
}
mAutoHideController.setNavigationBar(null);
+ mLightBarTransitionsController.destroy(mContext);
+ mLightBarController.setNavigationBar(null);
+ mPipOptional.ifPresent(this::removePipExclusionBoundsChangeListener);
+ mInitialized = false;
+ }
+
+ void addPipExclusionBoundsChangeListener(Pip pip) {
+ pip.addPipExclusionBoundsChangeListener(mPipListener);
+ }
+
+ void removePipExclusionBoundsChangeListener(Pip pip) {
+ pip.removePipExclusionBoundsChangeListener(mPipListener);
+ }
+
+ /**
+ * Returns {@code true} if this taskBar is {@link #init(int)}. Returns {@code false} if this
+ * taskbar has not yet been {@link #init(int)} or has been {@link #destroy()}.
+ */
+ public boolean isInitialized() {
+ return mInitialized;
}
private void updateSysuiFlags() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -194,10 +283,27 @@
.commitUpdate(mDisplayId);
}
+ private void updateAssistantAvailability(boolean assistantAvailable) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onAssistantAvailable() failed, available: " + assistantAvailable, e);
+ }
+ }
+
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
- boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+ boolean imeShown = mNavBarHelper.isImeShown(vis);
+ if (!imeShown) {
+ // Count imperceptible changes as visible so we transition taskbar out quickly.
+ imeShown = (vis & InputMethodService.IME_VISIBLE_IMPERCEPTIBLE) != 0;
+ }
+ showImeSwitcher = imeShown && showImeSwitcher;
int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
imeShown, showImeSwitcher);
if (hints != mNavigationIconHints) {
@@ -233,6 +339,10 @@
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
InsetsVisibilities requestedVisibilities, String packageName) {
mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
+ if (mLightBarController != null && displayId == mDisplayId) {
+ mLightBarController.onNavigationBarAppearanceChanged(appearance, false/*nbModeChanged*/,
+ BarTransitions.MODE_TRANSPARENT /*navigationBarMode*/, navbarColorManagedByIme);
+ }
if (mBehavior != behavior) {
mBehavior = behavior;
updateSysuiFlags();
@@ -273,6 +383,7 @@
@Override
public void onNavigationModeChanged(int mode) {
+ mNavigationMode = mode;
mEdgeBackGestureHandler.onNavigationModeChanged(mode);
}
@@ -293,9 +404,33 @@
public void onLowMemory() {}
@Override
+ public void showPinningEnterExitToast(boolean entering) {
+ updateSysuiFlags();
+ if (mScreenPinningNotify == null) {
+ return;
+ }
+ if (entering) {
+ mScreenPinningNotify.showPinningStartToast();
+ } else {
+ mScreenPinningNotify.showPinningExitToast();
+ }
+ }
+
+ @Override
+ public void showPinningEscapeToast() {
+ updateSysuiFlags();
+ if (mScreenPinningNotify == null) {
+ return;
+ }
+ mScreenPinningNotify.showEscapeToast(QuickStepContract.isGesturalMode(mNavigationMode),
+ !QuickStepContract.isGesturalMode(mNavigationMode));
+ }
+
+ @Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("TaskbarDelegate (displayId=" + mDisplayId + "):");
pw.println(" mNavigationIconHints=" + mNavigationIconHints);
+ pw.println(" mNavigationMode=" + mNavigationMode);
pw.println(" mDisabledFlags=" + mDisabledFlags);
pw.println(" mTaskBarWindowState=" + mTaskBarWindowState);
pw.println(" mBehavior=" + mBehavior);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index debd2eb..d27b716 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -96,6 +96,9 @@
@UiEvent(doc = "The overview button was pressed in the navigation bar.")
NAVBAR_OVERVIEW_BUTTON_TAP(535),
+ @UiEvent(doc = "The ime switcher button was pressed in the navigation bar.")
+ NAVBAR_IME_SWITCHER_BUTTON_TAP(923),
+
@UiEvent(doc = "The home button was long-pressed in the navigation bar.")
NAVBAR_HOME_BUTTON_LONGPRESS(536),
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 4d4962e..621075c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.navigationbar.gestural;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import android.app.ActivityManager;
@@ -548,7 +550,8 @@
layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
layoutParams.windowAnimations = 0;
layoutParams.privateFlags |=
- WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION);
layoutParams.setTitle(TAG + mContext.getDisplayId());
layoutParams.setFitInsetsTypes(0 /* types */);
layoutParams.setTrustedOverlay();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 98b9146..bc3a4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -116,7 +116,7 @@
}
} else if (v === powerMenuLite) {
uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
- globalActionsDialog.showOrHideDialog(false, true)
+ globalActionsDialog.showOrHideDialog(false, true, v)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
new file mode 100644
index 0000000..3b305bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -0,0 +1,146 @@
+package com.android.systemui.qs
+
+import android.view.View
+import com.android.internal.R
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyChipEvent
+import com.android.systemui.privacy.PrivacyDialogController
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import javax.inject.Inject
+
+interface ChipVisibilityListener {
+ fun onChipVisibilityRefreshed(visible: Boolean)
+}
+
+/**
+ * Controls privacy icons/chip residing in QS header which show up when app is using camera,
+ * microphone or location.
+ * Manages their visibility depending on privacy signals coming from [PrivacyItemController].
+ *
+ * Unlike typical controller extending [com.android.systemui.util.ViewController] this view doesn't
+ * observe its attachment state because depending on where it is used, it might be never detached.
+ * Instead, parent controller should use [onParentVisible] and [onParentInvisible] to "activate" or
+ * "deactivate" this controller.
+ */
+class HeaderPrivacyIconsController @Inject constructor(
+ private val privacyItemController: PrivacyItemController,
+ private val uiEventLogger: UiEventLogger,
+ private val privacyChip: OngoingPrivacyChip,
+ private val privacyDialogController: PrivacyDialogController,
+ private val privacyLogger: PrivacyLogger,
+ private val iconContainer: StatusIconContainer
+) {
+
+ var chipVisibilityListener: ChipVisibilityListener? = null
+ private var listening = false
+ private var micCameraIndicatorsEnabled = false
+ private var locationIndicatorsEnabled = false
+ private var privacyChipLogged = false
+ private val cameraSlot = privacyChip.resources.getString(R.string.status_bar_camera)
+ private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
+ private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
+
+ private val picCallback: PrivacyItemController.Callback =
+ object : PrivacyItemController.Callback {
+ override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ privacyChip.privacyList = privacyItems
+ setChipVisibility(privacyItems.isNotEmpty())
+ }
+
+ override fun onFlagMicCameraChanged(flag: Boolean) {
+ if (micCameraIndicatorsEnabled != flag) {
+ micCameraIndicatorsEnabled = flag
+ update()
+ }
+ }
+
+ override fun onFlagLocationChanged(flag: Boolean) {
+ if (locationIndicatorsEnabled != flag) {
+ locationIndicatorsEnabled = flag
+ update()
+ }
+ }
+
+ private fun update() {
+ updatePrivacyIconSlots()
+ setChipVisibility(privacyChip.privacyList.isNotEmpty())
+ }
+ }
+
+ private fun getChipEnabled() = micCameraIndicatorsEnabled || locationIndicatorsEnabled
+
+ fun onParentVisible() {
+ privacyChip.setOnClickListener {
+ // If the privacy chip is visible, it means there were some indicators
+ uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
+ privacyDialogController.showDialog(privacyChip.context)
+ }
+ setChipVisibility(privacyChip.visibility == View.VISIBLE)
+ micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
+ locationIndicatorsEnabled = privacyItemController.locationAvailable
+
+ // Ignore privacy icons because they show in the space above QQS
+ updatePrivacyIconSlots()
+ }
+
+ fun onParentInvisible() {
+ chipVisibilityListener = null
+ privacyChip.setOnClickListener(null)
+ }
+
+ fun startListening() {
+ listening = true
+ // Get the most up to date info
+ micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
+ locationIndicatorsEnabled = privacyItemController.locationAvailable
+ privacyItemController.addCallback(picCallback)
+ }
+
+ fun stopListening() {
+ listening = false
+ privacyItemController.removeCallback(picCallback)
+ privacyChipLogged = false
+ }
+
+ private fun setChipVisibility(visible: Boolean) {
+ if (visible && getChipEnabled()) {
+ privacyLogger.logChipVisible(true)
+ // Makes sure that the chip is logged as viewed at most once each time QS is opened
+ // mListening makes sure that the callback didn't return after the user closed QS
+ if (!privacyChipLogged && listening) {
+ privacyChipLogged = true
+ uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW)
+ }
+ } else {
+ privacyLogger.logChipVisible(false)
+ }
+
+ privacyChip.visibility = if (visible) View.VISIBLE else View.GONE
+ chipVisibilityListener?.onChipVisibilityRefreshed(visible)
+ }
+
+ private fun updatePrivacyIconSlots() {
+ if (getChipEnabled()) {
+ if (micCameraIndicatorsEnabled) {
+ iconContainer.addIgnoredSlot(cameraSlot)
+ iconContainer.addIgnoredSlot(micSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ }
+ if (locationIndicatorsEnabled) {
+ iconContainer.addIgnoredSlot(locationSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index cdf770f..c3de3c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -110,6 +110,16 @@
}
@Override
+ public int getTilesHeight() {
+ // Use the first page as that is the maximum height we need to show.
+ TileLayout tileLayout = mPages.get(0);
+ if (tileLayout == null) {
+ return 0;
+ }
+ return tileLayout.getTilesHeight();
+ }
+
+ @Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass configuration change to non-attached pages as well. Some config changes will cause
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
index 2f189be..768598a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -133,10 +133,7 @@
x += width + mHorizontalSpacing;
}
}
- y += maxHeight;
- if (row > 0) {
- y += mVerticalSpacing;
- }
+ y += maxHeight + mVerticalSpacing;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 8588ddf..e230e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -27,7 +27,6 @@
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -53,7 +52,6 @@
private float mQsExpansion;
private QSCustomizer mQSCustomizer;
private NonInterceptingScrollView mQSPanelContainer;
- private ImageView mDragHandle;
private int mSideMargins;
private boolean mQsDisabled;
@@ -71,7 +69,6 @@
mQSDetail = findViewById(R.id.qs_detail);
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
- mDragHandle = findViewById(R.id.qs_drag_handle);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
@@ -190,23 +187,14 @@
mQSDetail.setBottom(getTop() + scrollBottom);
int qsDetailBottomMargin = ((MarginLayoutParams) mQSDetail.getLayoutParams()).bottomMargin;
mQSDetail.setBottom(getTop() + scrollBottom - qsDetailBottomMargin);
- // Pin the drag handle to the bottom of the panel.
- mDragHandle.setTranslationY(scrollBottom - mDragHandle.getHeight());
}
protected int calculateContainerHeight() {
int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
// Need to add the dragHandle height so touches will be intercepted by it.
- int dragHandleHeight;
- if (mDragHandle.getVisibility() == VISIBLE) {
- dragHandleHeight = Math.round((1 - mQsExpansion) * mDragHandle.getHeight());
- } else {
- dragHandleHeight = 0;
- }
return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
: Math.round(mQsExpansion * (heightOverride - mHeader.getHeight()))
- + mHeader.getHeight()
- + dragHandleHeight;
+ + mHeader.getHeight();
}
int calculateContainerBottom() {
@@ -221,8 +209,6 @@
public void setExpansion(float expansion) {
mQsExpansion = expansion;
mQSPanelContainer.setScrollingEnabled(expansion > 0f);
- mDragHandle.setAlpha(1.0f - expansion);
- mDragHandle.setClickable(expansion == 0f); // Only clickable when fully collapsed
updateExpansion();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e42c47b..e82e9d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -33,7 +33,6 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout.LayoutParams;
-import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -94,7 +93,6 @@
private float mLastPanelFraction;
private float mSquishinessFraction = 1;
private boolean mQsDisabled;
- private ImageView mQsDragHandler;
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private final CommandQueue mCommandQueue;
@@ -205,7 +203,6 @@
mHeader = view.findViewById(R.id.header);
mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
mFooter = qsFragmentComponent.getQSFooter();
- mQsDragHandler = view.findViewById(R.id.qs_drag_handle);
mQsDetailDisplayer.setQsPanelController(mQSPanelController);
@@ -249,11 +246,6 @@
mQSPanelController.getMediaHost().getHostView().setAlpha(1.0f);
mQSAnimator.requestAnimatorUpdate();
});
-
- mQsDragHandler.setOnClickListener(v -> {
- Log.d(TAG, "drag handler clicked");
- mCommandQueue.animateExpandSettingsPanel(null);
- });
}
@Override
@@ -385,30 +377,26 @@
}
private void updateQsState() {
- final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling
+ final boolean expanded = mQsExpanded || mInSplitShade;
+ final boolean expandVisually = expanded || mStackScrollerOverscrolling
|| mHeaderAnimating;
- mQSPanelController.setExpanded(mQsExpanded);
- mQSDetail.setExpanded(mQsExpanded);
+ mQSPanelController.setExpanded(expanded);
+ mQSDetail.setExpanded(expanded);
boolean keyguardShowing = isKeyguardState();
- mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
+ mHeader.setVisibility((expanded || !keyguardShowing || mHeaderAnimating
|| mShowCollapsedOnKeyguard)
? View.VISIBLE
: View.INVISIBLE);
mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
- || (mQsExpanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
- mFooter.setVisibility(!mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating
+ || (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
+ mFooter.setVisibility(!mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
|| mShowCollapsedOnKeyguard)
? View.VISIBLE
: View.INVISIBLE);
mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
- || (mQsExpanded && !mStackScrollerOverscrolling));
+ || (expanded && !mStackScrollerOverscrolling));
mQSPanelController.setVisibility(
!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
- mQsDragHandler.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
- || mShowCollapsedOnKeyguard)
- && Utils.shouldUseSplitNotificationShade(getResources())
- ? View.VISIBLE
- : View.GONE);
}
private boolean isKeyguardState() {
@@ -418,7 +406,8 @@
}
private void updateShowCollapsedOnKeyguard() {
- boolean showCollapsed = mBypassController.getBypassEnabled() || mTransitioningToFullShade;
+ boolean showCollapsed = mBypassController.getBypassEnabled()
+ || (mTransitioningToFullShade && !mInSplitShade);
if (showCollapsed != mShowCollapsedOnKeyguard) {
mShowCollapsedOnKeyguard = showCollapsed;
updateQsState();
@@ -498,6 +487,8 @@
public void setInSplitShade(boolean inSplitShade) {
mInSplitShade = inSplitShade;
mQSAnimator.setTranslateWhileExpanding(inSplitShade);
+ updateShowCollapsedOnKeyguard();
+ updateQsState();
}
@Override
@@ -516,7 +507,8 @@
public void setQsExpansion(float expansion, float panelExpansionFraction,
float proposedTranslation, float squishinessFraction) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
- float progress = mTransitioningToFullShade ? mFullShadeProgress : panelExpansionFraction;
+ float progress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
+ ? mFullShadeProgress : panelExpansionFraction;
setAlphaAnimationProgress(mInSplitShade ? progress : 1);
mContainer.setExpansion(expansion);
final float translationScaleY = (mInSplitShade
@@ -770,6 +762,8 @@
public void onAnimationEnd(Animator animation) {
mHeaderAnimating = false;
updateQsState();
+ // Unset the listener, otherwise this may persist for another view property animation
+ getView().animate().setListener(null);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 71eb4a2..20c0fdd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -26,6 +26,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -103,6 +104,8 @@
protected LinearLayout mHorizontalContentContainer;
protected QSTileLayout mTileLayout;
+ private float mSquishinessFraction = 1f;
+ private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -179,10 +182,26 @@
if (mTileLayout == null) {
mTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
.inflate(R.layout.qs_paged_tile_layout, this, false);
+ mTileLayout.setSquishinessFraction(mSquishinessFraction);
}
return mTileLayout;
}
+ public void setSquishinessFraction(float squishinessFraction) {
+ if (Float.compare(squishinessFraction, mSquishinessFraction) == 0) {
+ return;
+ }
+ mSquishinessFraction = squishinessFraction;
+ if (mTileLayout == null) {
+ return;
+ }
+ mTileLayout.setSquishinessFraction(squishinessFraction);
+ if (getMeasuredWidth() == 0) {
+ return;
+ }
+ updateViewPositions();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mTileLayout instanceof PagedTileLayout) {
@@ -228,6 +247,34 @@
setMeasuredDimension(getMeasuredWidth(), height);
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ mChildrenLayoutTop.put(child, child.getTop());
+ }
+ updateViewPositions();
+ }
+
+ private void updateViewPositions() {
+ // Adjust view positions based on tile squishing
+ int tileHeightOffset = mTileLayout.getTilesHeight() - mTileLayout.getHeight();
+
+ boolean move = false;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (move) {
+ int top = mChildrenLayoutTop.get(child);
+ child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset,
+ child.getRight(), top + tileHeightOffset + child.getHeight());
+ }
+ if (child == mTileLayout) {
+ move = true;
+ }
+ }
+ }
+
protected String getDumpableTag() {
return TAG;
}
@@ -735,6 +782,12 @@
/** */
void setListening(boolean listening, UiEventLogger uiEventLogger);
+ /** */
+ int getHeight();
+
+ /** */
+ int getTilesHeight();
+
/**
* Sets a size modifier for the tile. Where 0 means collapsed, and 1 expanded.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 6794d5b..001c740e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.Utils;
import javax.inject.Inject;
import javax.inject.Named;
@@ -72,6 +73,7 @@
new QSPanel.OnConfigurationChangedListener() {
@Override
public void onConfigurationChange(Configuration newConfig) {
+ updateMediaExpansion();
mView.updateResources();
mQsSecurityFooter.onConfigurationChanged();
if (mView.isListening()) {
@@ -121,13 +123,17 @@
@Override
public void onInit() {
super.onInit();
- mMediaHost.setExpansion(1);
+ updateMediaExpansion();
mMediaHost.setShowsOnlyActiveMedia(false);
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
mQsCustomizerController.init();
mBrightnessSliderController.init();
}
+ private void updateMediaExpansion() {
+ mMediaHost.setExpansion(Utils.shouldUseSplitNotificationShade(getResources()) ? 0 : 1);
+ }
+
@Override
protected void onViewAttached() {
super.onViewAttached();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index f7d1b1e..eddc206 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -147,6 +147,10 @@
return mMediaHost;
}
+ public void setSquishinessFraction(float squishinessFraction) {
+ mView.setSquishinessFraction(squishinessFraction);
+ }
+
@Override
protected void onViewAttached() {
mQsTileRevealController = createTileRevealController();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
index c1c146d..c680cb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
@@ -1,14 +1,10 @@
package com.android.systemui.qs
-import android.view.ViewGroup
-import com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER
import com.android.systemui.qs.dagger.QSScope
import javax.inject.Inject
-import javax.inject.Named
@QSScope
class QSSquishinessController @Inject constructor(
- @Named(QQS_FOOTER) private val qqsFooterActionsView: FooterActionsView,
private val qsAnimator: QSAnimator,
private val qsPanelController: QSPanelController,
private val quickQSPanelController: QuickQSPanelController
@@ -33,23 +29,7 @@
* Change the height of all tiles and repositions their siblings.
*/
private fun updateSquishiness() {
- (qsPanelController.tileLayout as QSPanel.QSTileLayout).setSquishinessFraction(squishiness)
- val tileLayout = quickQSPanelController.tileLayout as TileLayout
- tileLayout.setSquishinessFraction(squishiness)
-
- // Calculate how much we should move the footer
- val tileHeightOffset = tileLayout.height - tileLayout.tilesHeight
- val footerTopMargin = (qqsFooterActionsView.layoutParams as ViewGroup.MarginLayoutParams)
- .topMargin
- val nextTop = tileLayout.bottom - tileHeightOffset + footerTopMargin
- val amountMoved = nextTop - qqsFooterActionsView.top
-
- // Move the footer and other siblings (MediaPlayer)
- (qqsFooterActionsView.parent as ViewGroup?)?.let { parent ->
- val index = parent.indexOfChild(qqsFooterActionsView)
- for (i in index until parent.childCount) {
- parent.getChildAt(i).top += amountMoved
- }
- }
+ qsPanelController.setSquishinessFraction(squishiness)
+ quickQSPanelController.setSquishinessFraction(squishiness)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a923eff..3bf8bee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -373,7 +373,6 @@
}
void setChipVisibility(boolean visibility) {
- mPrivacyChip.setVisibility(visibility ? View.VISIBLE : View.GONE);
if (visibility) {
// Animates the icons and battery indicator from alpha 0 to 1, when the chip is visible
mIconsAlphaAnimator = mIconsAlphaAnimatorFixed;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 3a80764..2dc4ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -17,26 +17,14 @@
package com.android.systemui.qs;
import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-
-import androidx.annotation.NonNull;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.privacy.PrivacyChipEvent;
-import com.android.systemui.privacy.PrivacyDialogController;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
@@ -54,23 +42,17 @@
* Controller for {@link QuickStatusBarHeader}.
*/
@QSScope
-class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
- private static final String TAG = "QuickStatusBarHeader";
+class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> implements
+ ChipVisibilityListener {
- private final PrivacyItemController mPrivacyItemController;
- private final ActivityStarter mActivityStarter;
- private final UiEventLogger mUiEventLogger;
private final QSCarrierGroupController mQSCarrierGroupController;
private final QuickQSPanelController mQuickQSPanelController;
- private final OngoingPrivacyChip mPrivacyChip;
private final Clock mClockView;
private final StatusBarIconController mStatusBarIconController;
private final DemoModeController mDemoModeController;
private final StatusIconContainer mIconContainer;
private final StatusBarIconController.TintedIconManager mIconManager;
private final DemoMode mDemoModeReceiver;
- private final PrivacyLogger mPrivacyLogger;
- private final PrivacyDialogController mPrivacyDialogController;
private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private final BatteryMeterViewController mBatteryMeterViewController;
private final FeatureFlags mFeatureFlags;
@@ -78,83 +60,31 @@
private final VariableDateViewController mVariableDateViewControllerDateView;
private final VariableDateViewController mVariableDateViewControllerClockDateView;
+ private final HeaderPrivacyIconsController mPrivacyIconsController;
private boolean mListening;
- private boolean mMicCameraIndicatorsEnabled;
- private boolean mLocationIndicatorsEnabled;
- private boolean mPrivacyChipLogged;
- private final String mCameraSlot;
- private final String mMicSlot;
- private final String mLocationSlot;
private SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
- private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
- @Override
- public void onPrivacyItemsChanged(@NonNull List<PrivacyItem> privacyItems) {
- mPrivacyChip.setPrivacyList(privacyItems);
- setChipVisibility(!privacyItems.isEmpty());
- }
-
- @Override
- public void onFlagMicCameraChanged(boolean flag) {
- if (mMicCameraIndicatorsEnabled != flag) {
- mMicCameraIndicatorsEnabled = flag;
- update();
- }
- }
-
- @Override
- public void onFlagLocationChanged(boolean flag) {
- if (mLocationIndicatorsEnabled != flag) {
- mLocationIndicatorsEnabled = flag;
- update();
- }
- }
-
- private void update() {
- updatePrivacyIconSlots();
- setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
- }
- };
-
- private View.OnClickListener mOnClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (v == mPrivacyChip) {
- // If the privacy chip is visible, it means there were some indicators
- mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
- mPrivacyDialogController.showDialog(getContext());
- }
- }
- };
-
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
- PrivacyItemController privacyItemController,
- ActivityStarter activityStarter, UiEventLogger uiEventLogger,
+ HeaderPrivacyIconsController headerPrivacyIconsController,
StatusBarIconController statusBarIconController,
DemoModeController demoModeController,
QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
- PrivacyLogger privacyLogger,
SysuiColorExtractor colorExtractor,
- PrivacyDialogController privacyDialogController,
QSExpansionPathInterpolator qsExpansionPathInterpolator,
BatteryMeterViewController batteryMeterViewController,
FeatureFlags featureFlags,
VariableDateViewController.Factory variableDateViewControllerFactory,
StatusBarContentInsetsProvider statusBarContentInsetsProvider) {
super(view);
- mPrivacyItemController = privacyItemController;
- mActivityStarter = activityStarter;
- mUiEventLogger = uiEventLogger;
+ mPrivacyIconsController = headerPrivacyIconsController;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
mQuickQSPanelController = quickQSPanelController;
- mPrivacyLogger = privacyLogger;
- mPrivacyDialogController = privacyDialogController;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mBatteryMeterViewController = batteryMeterViewController;
mFeatureFlags = featureFlags;
@@ -163,8 +93,6 @@
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
.build();
-
- mPrivacyChip = mView.findViewById(R.id.privacy_chip);
mClockView = mView.findViewById(R.id.clock);
mIconContainer = mView.findViewById(R.id.statusIcons);
mVariableDateViewControllerDateView = variableDateViewControllerFactory.create(
@@ -183,10 +111,6 @@
};
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
- mCameraSlot = getResources().getString(com.android.internal.R.string.status_bar_camera);
- mMicSlot = getResources().getString(com.android.internal.R.string.status_bar_microphone);
- mLocationSlot = getResources().getString(com.android.internal.R.string.status_bar_location);
-
// Don't need to worry about tuner settings for this icon
mBatteryMeterViewController.ignoreTunerUpdates();
}
@@ -198,20 +122,13 @@
@Override
protected void onViewAttached() {
- mPrivacyChip.setOnClickListener(mOnClickListener);
-
- mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
- mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
-
- // Ignore privacy icons because they show in the space above QQS
- updatePrivacyIconSlots();
+ mPrivacyIconsController.onParentVisible();
+ mPrivacyIconsController.setChipVisibilityListener(this);
mIconContainer.addIgnoredSlot(
getResources().getString(com.android.internal.R.string.status_bar_managed_profile));
mIconContainer.setShouldRestrictIcons(false);
mStatusBarIconController.addIconGroup(mIconManager);
- setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
-
mView.setIsSingleCarrier(mQSCarrierGroupController.isSingleCarrier());
mQSCarrierGroupController
.setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
@@ -241,7 +158,7 @@
@Override
protected void onViewDetached() {
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
- mPrivacyChip.setOnClickListener(null);
+ mPrivacyIconsController.onParentInvisible();
mStatusBarIconController.removeIconGroup(mIconManager);
mQSCarrierGroupController.setOnSingleCarrierChangedListener(null);
mDemoModeController.removeCallback(mDemoModeReceiver);
@@ -266,54 +183,15 @@
}
if (listening) {
- // Get the most up to date info
- mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
- mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
- mPrivacyItemController.addCallback(mPICCallback);
+ mPrivacyIconsController.startListening();
} else {
- mPrivacyItemController.removeCallback(mPICCallback);
- mPrivacyChipLogged = false;
+ mPrivacyIconsController.stopListening();
}
}
- private void setChipVisibility(boolean chipVisible) {
- if (chipVisible && getChipEnabled()) {
- mPrivacyLogger.logChipVisible(true);
- // Makes sure that the chip is logged as viewed at most once each time QS is opened
- // mListening makes sure that the callback didn't return after the user closed QS
- if (!mPrivacyChipLogged && mListening) {
- mPrivacyChipLogged = true;
- mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
- }
- } else {
- mPrivacyLogger.logChipVisible(false);
- }
- mView.setChipVisibility(chipVisible);
- }
-
- private void updatePrivacyIconSlots() {
- if (getChipEnabled()) {
- if (mMicCameraIndicatorsEnabled) {
- mIconContainer.addIgnoredSlot(mCameraSlot);
- mIconContainer.addIgnoredSlot(mMicSlot);
- } else {
- mIconContainer.removeIgnoredSlot(mCameraSlot);
- mIconContainer.removeIgnoredSlot(mMicSlot);
- }
- if (mLocationIndicatorsEnabled) {
- mIconContainer.addIgnoredSlot(mLocationSlot);
- } else {
- mIconContainer.removeIgnoredSlot(mLocationSlot);
- }
- } else {
- mIconContainer.removeIgnoredSlot(mCameraSlot);
- mIconContainer.removeIgnoredSlot(mMicSlot);
- mIconContainer.removeIgnoredSlot(mLocationSlot);
- }
- }
-
- private boolean getChipEnabled() {
- return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
+ @Override
+ public void onChipVisibilityRefreshed(boolean visible) {
+ mView.setChipVisibility(visible);
}
public void setContentMargins(int marginStart, int marginEnd) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 7f08e5b..bff318a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -275,6 +275,7 @@
return Math.max(mColumns * mRows, 1);
}
+ @Override
public int getTilesHeight() {
return mLastTileBottom + getPaddingBottom();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index b11420a..11e5b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -26,6 +26,7 @@
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.FooterActionsController;
import com.android.systemui.qs.FooterActionsController.ExpansionState;
import com.android.systemui.qs.FooterActionsControllerBuilder;
@@ -39,6 +40,7 @@
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
import javax.inject.Named;
@@ -189,4 +191,18 @@
static boolean providesQSUsingMediaPlayer(Context context) {
return useQsMediaPlayer(context);
}
+
+ /** */
+ @Provides
+ @QSScope
+ static OngoingPrivacyChip providesPrivacyChip(QuickStatusBarHeader qsHeader) {
+ return qsHeader.findViewById(R.id.privacy_chip);
+ }
+
+ /** */
+ @Provides
+ @QSScope
+ static StatusIconContainer providesStatusIconContainer(QuickStatusBarHeader qsHeader) {
+ return qsHeader.findViewById(R.id.statusIcons);
+ }
}
\ No newline at end of file
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 2bd5c8f..9d60e63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -247,7 +247,10 @@
} else {
measuredHeight
}
- bottom = top + (actualHeight * squishinessFraction).toInt()
+ // Limit how much we affect the height, so we don't have rounding artifacts when the tile
+ // is too short.
+ val constrainedSquishiness = 0.1f + squishinessFraction * 0.9f
+ bottom = top + (actualHeight * constrainedSquishiness).toInt()
scrollY = (actualHeight - height) / 2
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 0427e38..b83dc52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -31,7 +31,6 @@
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
-import android.view.WindowManager.LayoutParams;
import android.widget.Button;
import androidx.annotation.Nullable;
@@ -40,6 +39,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -76,6 +76,7 @@
private final CastDetailAdapter mDetailAdapter;
private final KeyguardStateController mKeyguard;
private final NetworkController mNetworkController;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private final Callback mCallback = new Callback();
private Dialog mDialog;
private boolean mWifiConnected;
@@ -94,7 +95,8 @@
CastController castController,
KeyguardStateController keyguardStateController,
NetworkController networkController,
- HotspotController hotspotController
+ HotspotController hotspotController,
+ DialogLaunchAnimator dialogLaunchAnimator
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -102,6 +104,7 @@
mDetailAdapter = new CastDetailAdapter();
mKeyguard = keyguardStateController;
mNetworkController = networkController;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
mNetworkController.observe(this, mSignalCallback);
@@ -153,9 +156,15 @@
List<CastDevice> activeDevices = getActiveDevices();
if (willPopDetail()) {
- mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
- showDetail(true);
- });
+ if (!mKeyguard.isShowing()) {
+ showDetail(view);
+ } else {
+ mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+ // Dismissing the keyguard will collapse the shade, so we don't animate from the
+ // view here as it would not look good.
+ showDetail(null /* view */);
+ });
+ }
} else {
mController.stopCasting(activeDevices.get(0));
}
@@ -184,19 +193,29 @@
@Override
public void showDetail(boolean show) {
+ showDetail(null /* view */);
+ }
+
+ private void showDetail(@Nullable View view) {
mUiHandler.post(() -> {
mDialog = MediaRouteDialogPresenter.createDialog(mContext, ROUTE_TYPE_REMOTE_DISPLAY,
v -> {
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
mActivityStarter
.postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
});
- mDialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
SystemUIDialog.setShowForAllUsers(mDialog, true);
SystemUIDialog.registerDismissListener(mDialog);
SystemUIDialog.setWindowOnTop(mDialog);
- mUiHandler.post(() -> mDialog.show());
- mHost.collapsePanels();
+
+ mUiHandler.post(() -> {
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(mDialog, view);
+ } else {
+ mDialog.show();
+ }
+ });
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 7ba9cc2..a2577d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -98,7 +98,7 @@
toggleDataSaver();
Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true);
});
- dialog.setNegativeButton(com.android.internal.R.string.cancel, null);
+ dialog.setNeutralButton(com.android.internal.R.string.cancel, null);
dialog.setShowForAllUsers(true);
if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index b1cd68e..a339dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -19,6 +19,7 @@
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
@@ -41,7 +42,6 @@
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.widget.Switch;
import android.widget.Toast;
@@ -53,6 +53,7 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysUIToast;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -84,6 +85,7 @@
private final DndDetailAdapter mDetailAdapter;
private final SharedPreferences mSharedPreferences;
private final SecureSetting mSettingZenDuration;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private boolean mListening;
private boolean mShowingDetail;
@@ -100,7 +102,8 @@
QSLogger qsLogger,
ZenModeController zenModeController,
@Main SharedPreferences sharedPreferences,
- SecureSettings secureSettings
+ SecureSettings secureSettings,
+ DialogLaunchAnimator dialogLaunchAnimator
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -108,6 +111,7 @@
mSharedPreferences = sharedPreferences;
mDetailAdapter = new DndDetailAdapter();
mController.observe(getLifecycle(), mZenCallback);
+ mDialogLaunchAnimator = dialogLaunchAnimator;
mSettingZenDuration = new SecureSetting(secureSettings, mUiHandler,
Settings.Secure.ZEN_DURATION, getHost().getUserId()) {
@Override
@@ -117,8 +121,6 @@
};
}
-
-
public static void setVisible(Context context, boolean visible) {
Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
}
@@ -187,14 +189,12 @@
switch (zenDuration) {
case Settings.Secure.ZEN_DURATION_PROMPT:
mUiHandler.post(() -> {
- Dialog mDialog = new EnableZenModeDialog(mContext).createDialog();
- mDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- SystemUIDialog.setShowForAllUsers(mDialog, true);
- SystemUIDialog.registerDismissListener(mDialog);
- SystemUIDialog.setWindowOnTop(mDialog);
- mUiHandler.post(() -> mDialog.show());
- mHost.collapsePanels();
+ Dialog dialog = makeZenModeDialog();
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(dialog, view, false);
+ } else {
+ dialog.show();
+ }
});
break;
case Settings.Secure.ZEN_DURATION_FOREVER:
@@ -209,6 +209,16 @@
}
}
+ private Dialog makeZenModeDialog() {
+ AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog,
+ true /* cancelIsNeutral */).createDialog();
+ SystemUIDialog.applyFlags(dialog);
+ SystemUIDialog.setShowForAllUsers(dialog, true);
+ SystemUIDialog.registerDismissListener(dialog);
+ SystemUIDialog.setDialogSize(dialog);
+ return dialog;
+ }
+
@Override
protected void handleSecondaryClick(@Nullable View view) {
if (mController.isVolumeRestricted()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 99eb5b6..544246e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -53,7 +53,10 @@
private final InternetDialogController mInternetDialogController;
private List<WifiEntry> mWifiEntries;
- private int mWifiEntriesCount;
+ @VisibleForTesting
+ protected int mWifiEntriesCount;
+ @VisibleForTesting
+ protected int mMaxEntriesCount = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
protected View mHolderView;
protected Context mContext;
@@ -87,7 +90,8 @@
*/
public void setWifiEntries(@Nullable List<WifiEntry> wifiEntries, int wifiEntriesCount) {
mWifiEntries = wifiEntries;
- mWifiEntriesCount = wifiEntriesCount;
+ mWifiEntriesCount =
+ (wifiEntriesCount < mMaxEntriesCount) ? wifiEntriesCount : mMaxEntriesCount;
}
/**
@@ -101,6 +105,20 @@
}
/**
+ * Sets the maximum number of Wi-Fi networks.
+ */
+ public void setMaxEntriesCount(int count) {
+ if (count < 0 || mMaxEntriesCount == count) {
+ return;
+ }
+ mMaxEntriesCount = count;
+ if (mWifiEntriesCount > count) {
+ mWifiEntriesCount = count;
+ notifyDataSetChanged();
+ }
+ }
+
+ /**
* ViewHolder for binding Wi-Fi view.
*/
static class InternetViewHolder extends RecyclerView.ViewHolder {
@@ -133,7 +151,8 @@
}
void onBind(@NonNull WifiEntry wifiEntry) {
- mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
+ mWifiIcon.setImageDrawable(
+ getWifiDrawable(wifiEntry.getLevel(), wifiEntry.shouldShowXLevelIcon()));
setWifiNetworkLayout(wifiEntry.getTitle(),
Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
@@ -170,12 +189,13 @@
mWifiSummaryText.setText(summary);
}
- Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) {
- if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
+ Drawable getWifiDrawable(int level, boolean hasNoInternet) {
+ // If the Wi-Fi level is equal to WIFI_LEVEL_UNREACHABLE(-1), then a null drawable
+ // will be returned.
+ if (level == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
return null;
}
- final Drawable drawable = mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(),
- wifiEntry.getLevel());
+ final Drawable drawable = mWifiIconInjector.getIcon(hasNoInternet, level);
if (drawable == null) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 77b9cc1..ef4d1ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.dialog;
import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
import android.app.AlertDialog;
import android.content.Context;
@@ -40,6 +41,7 @@
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -56,6 +58,7 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
@@ -77,7 +80,8 @@
private static final String TAG = "InternetDialog";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- static final long PROGRESS_DELAY_MS = 2000L;
+ static final long PROGRESS_DELAY_MS = 1500L;
+ static final int MAX_NETWORK_COUNT = 4;
private final Handler mHandler;
private final Executor mBackgroundExecutor;
@@ -119,19 +123,25 @@
private ImageView mSignalIcon;
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
+ private TextView mAirplaneModeSummaryText;
private Switch mMobileDataToggle;
+ private View mMobileToggleDivider;
private Switch mWiFiToggle;
- private FrameLayout mDoneLayout;
+ private Button mDoneButton;
+ private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mCanConfigMobileData;
// Wi-Fi entries
+ private int mWifiNetworkHeight;
@VisibleForTesting
protected WifiEntry mConnectedWifiEntry;
@VisibleForTesting
protected int mWifiEntriesCount;
+ @VisibleForTesting
+ protected boolean mHasMoreWifiEntries;
// Wi-Fi scanning progress bar
protected boolean mIsProgressBarVisible;
@@ -152,7 +162,9 @@
if (DEBUG) {
Log.d(TAG, "Init InternetDialog");
}
- mContext = context;
+
+ // Save the context that is wrapped with our theme.
+ mContext = getContext();
mHandler = handler;
mBackgroundExecutor = executor;
mInternetDialogFactory = internetDialogFactory;
@@ -185,6 +197,9 @@
window.setWindowAnimations(R.style.Animation_InternetDialog);
+ mWifiNetworkHeight = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height);
+
mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog);
mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
@@ -203,10 +218,13 @@
mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
- mDoneLayout = mDialogView.requireViewById(R.id.done_layout);
+ mDoneButton = mDialogView.requireViewById(R.id.done_button);
+ mAirplaneModeButton = mDialogView.requireViewById(R.id.apm_button);
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+ mAirplaneModeSummaryText = mDialogView.requireViewById(R.id.airplane_mode_summary);
+ mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
@@ -223,6 +241,8 @@
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
+ mAirplaneModeButton.setVisibility(
+ mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
mWifiRecyclerView.setAdapter(mAdapter);
}
@@ -261,7 +281,8 @@
mConnectedWifListLayout.setOnClickListener(null);
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
- mDoneLayout.setOnClickListener(null);
+ mDoneButton.setOnClickListener(null);
+ mAirplaneModeButton.setOnClickListener(null);
mInternetDialogController.onStop();
mInternetDialogFactory.destroyDialog();
}
@@ -285,15 +306,15 @@
if (DEBUG) {
Log.d(TAG, "updateDialog");
}
- if (mInternetDialogController.isAirplaneModeEnabled()) {
- mInternetDialogSubTitle.setVisibility(View.GONE);
- } else {
- mInternetDialogSubTitle.setText(getSubtitleText());
- }
+ mInternetDialogTitle.setText(getDialogTitleText());
+ mInternetDialogSubTitle.setText(getSubtitleText());
+ mAirplaneModeButton.setVisibility(
+ mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
+
updateEthernet();
if (shouldUpdateMobileNetwork) {
- setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular()
- || mInternetDialogController.isCarrierNetworkActive());
+ setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular(),
+ mInternetDialogController.isCarrierNetworkActive());
}
if (!mCanConfigWifi) {
@@ -332,13 +353,13 @@
mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
mWiFiToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
- if (isChecked) {
- mWifiScanNotifyLayout.setVisibility(View.GONE);
- }
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
});
- mDoneLayout.setOnClickListener(v -> dismiss());
+ mDoneButton.setOnClickListener(v -> dismiss());
+ mAirplaneModeButton.setOnClickListener(v -> {
+ mInternetDialogController.setAirplaneModeDisabled();
+ });
}
@MainThread
@@ -347,38 +368,66 @@
mInternetDialogController.hasEthernet() ? View.VISIBLE : View.GONE);
}
- private void setMobileDataLayout(boolean isCarrierNetworkConnected) {
- if (mInternetDialogController.isAirplaneModeEnabled()
- || !mInternetDialogController.hasCarrier()) {
+ private void setMobileDataLayout(boolean activeNetworkIsCellular,
+ boolean isCarrierNetworkActive) {
+ boolean isNetworkConnected = activeNetworkIsCellular || isCarrierNetworkActive;
+ // 1. Mobile network should be gone if airplane mode ON or the list of active
+ // subscriptionId is null.
+ // 2. Carrier network should be gone if airplane mode ON and Wi-Fi is OFF.
+ if (DEBUG) {
+ Log.d(TAG, "setMobileDataLayout, isCarrierNetworkActive = " + isCarrierNetworkActive);
+ }
+
+ if (!mInternetDialogController.hasActiveSubId()
+ && (!mWifiManager.isWifiEnabled() || !isCarrierNetworkActive)) {
mMobileNetworkLayout.setVisibility(View.GONE);
} else {
- mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
mMobileNetworkLayout.setVisibility(View.VISIBLE);
+ mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
mMobileTitleText.setText(getMobileNetworkTitle());
- if (!TextUtils.isEmpty(getMobileNetworkSummary())) {
+ String summary = getMobileNetworkSummary();
+ if (!TextUtils.isEmpty(summary)) {
mMobileSummaryText.setText(
- Html.fromHtml(getMobileNetworkSummary(), Html.FROM_HTML_MODE_LEGACY));
+ Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY));
mMobileSummaryText.setVisibility(View.VISIBLE);
} else {
mMobileSummaryText.setVisibility(View.GONE);
}
-
mBackgroundExecutor.execute(() -> {
Drawable drawable = getSignalStrengthDrawable();
mHandler.post(() -> {
mSignalIcon.setImageDrawable(drawable);
});
});
- mMobileTitleText.setTextAppearance(isCarrierNetworkConnected
+ mMobileTitleText.setTextAppearance(isNetworkConnected
? R.style.TextAppearance_InternetDialog_Active
: R.style.TextAppearance_InternetDialog);
- mMobileSummaryText.setTextAppearance(isCarrierNetworkConnected
+ int secondaryRes = isNetworkConnected
? R.style.TextAppearance_InternetDialog_Secondary_Active
- : R.style.TextAppearance_InternetDialog_Secondary);
+ : R.style.TextAppearance_InternetDialog_Secondary;
+ mMobileSummaryText.setTextAppearance(secondaryRes);
+ // Set airplane mode to the summary for carrier network
+ if (mInternetDialogController.isAirplaneModeEnabled()) {
+ mAirplaneModeSummaryText.setVisibility(View.VISIBLE);
+ mAirplaneModeSummaryText.setText(mContext.getText(R.string.airplane_mode));
+ mAirplaneModeSummaryText.setTextAppearance(secondaryRes);
+ } else {
+ mAirplaneModeSummaryText.setVisibility(View.GONE);
+ }
mMobileNetworkLayout.setBackground(
- isCarrierNetworkConnected ? mBackgroundOn : mBackgroundOff);
+ isNetworkConnected ? mBackgroundOn : mBackgroundOff);
+
+ TypedArray array = mContext.obtainStyledAttributes(
+ R.style.InternetDialog_Divider_Active, new int[]{android.R.attr.background});
+ int dividerColor = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.textColorSecondary);
+ mMobileToggleDivider.setBackgroundColor(isNetworkConnected
+ ? array.getColor(0, dividerColor) : dividerColor);
+ array.recycle();
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+ mMobileToggleDivider.setVisibility(
+ mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
}
}
@@ -416,9 +465,40 @@
mSeeAllLayout.setVisibility(View.GONE);
return;
}
- mWifiRecyclerView.setVisibility(mWifiEntriesCount > 0 ? View.VISIBLE : View.GONE);
- mSeeAllLayout.setVisibility(
- (mConnectedWifiEntry != null || mWifiEntriesCount > 0) ? View.VISIBLE : View.GONE);
+ final int wifiListMaxCount = getWifiListMaxCount();
+ if (mAdapter.getItemCount() > wifiListMaxCount) {
+ mHasMoreWifiEntries = true;
+ }
+ mAdapter.setMaxEntriesCount(wifiListMaxCount);
+ final int wifiListMinHeight = mWifiNetworkHeight * wifiListMaxCount;
+ if (mWifiRecyclerView.getMinimumHeight() != wifiListMinHeight) {
+ mWifiRecyclerView.setMinimumHeight(wifiListMinHeight);
+ }
+ mWifiRecyclerView.setVisibility(View.VISIBLE);
+ mSeeAllLayout.setVisibility(mHasMoreWifiEntries ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @VisibleForTesting
+ @MainThread
+ int getWifiListMaxCount() {
+ // Use the maximum count of networks to calculate the remaining count for Wi-Fi networks.
+ int count = MAX_NETWORK_COUNT;
+ if (mEthernetLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+ if (mMobileNetworkLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+
+ // If the remaining count is greater than the maximum count of the Wi-Fi network, the
+ // maximum count of the Wi-Fi network is used.
+ if (count > MAX_WIFI_ENTRY_COUNT) {
+ count = MAX_WIFI_ENTRY_COUNT;
+ }
+ if (mConnectedWifListLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+ return count;
}
@MainThread
@@ -486,9 +566,13 @@
}
private void setProgressBarVisible(boolean visible) {
+ if (mIsProgressBarVisible == visible) {
+ return;
+ }
mIsProgressBarVisible = visible;
- mProgressBar.setVisibility(mIsProgressBarVisible ? View.VISIBLE : View.GONE);
- mDivider.setVisibility(mIsProgressBarVisible ? View.GONE : View.VISIBLE);
+ mProgressBar.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mProgressBar.setIndeterminate(visible);
+ mDivider.setVisibility(visible ? View.GONE : View.VISIBLE);
mInternetDialogSubTitle.setText(getSubtitleText());
}
@@ -588,11 +672,15 @@
@Override
@WorkerThread
public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
- @Nullable WifiEntry connectedEntry) {
+ @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries) {
+ // Should update the carrier network layout when it is connected under airplane mode ON.
+ boolean shouldUpdateCarrierNetwork = mMobileNetworkLayout.getVisibility() == View.VISIBLE
+ && mInternetDialogController.isAirplaneModeEnabled();
mHandler.post(() -> {
mConnectedWifiEntry = connectedEntry;
mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
- updateDialog(false /* shouldUpdateMobileNetwork */);
+ mHasMoreWifiEntries = hasMoreWifiEntries;
+ updateDialog(shouldUpdateCarrierNetwork /* shouldUpdateMobileNetwork */);
mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
mAdapter.notifyDataSetChanged();
});
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 2a7d2c3..0d064af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -18,6 +18,7 @@
import static com.android.settingslib.mobile.MobileMappings.getIconKey;
import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -86,6 +87,7 @@
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -98,8 +100,10 @@
import javax.inject.Inject;
-public class InternetDialogController implements WifiEntry.DisconnectCallback,
- AccessPointController.AccessPointCallback {
+/**
+ * Controller for Internet Dialog.
+ */
+public class InternetDialogController implements AccessPointController.AccessPointCallback {
private static final String TAG = "InternetDialogController";
private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
@@ -122,7 +126,7 @@
R.string.all_network_unavailable;
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- static final int MAX_WIFI_ENTRY_COUNT = 4;
+ static final int MAX_WIFI_ENTRY_COUNT = 3;
private WifiManager mWifiManager;
private Context mContext;
@@ -140,8 +144,6 @@
private AccessPointController mAccessPointController;
private IntentFilter mConnectionStateFilter;
private InternetDialogCallback mCallback;
- private WifiEntry mConnectedEntry;
- private int mWifiEntriesCount;
private UiEventLogger mUiEventLogger;
private BroadcastDispatcher mBroadcastDispatcher;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -153,6 +155,7 @@
private SignalDrawable mSignalDrawable;
private LocationController mLocationController;
private DialogLaunchAnimator mDialogLaunchAnimator;
+ private boolean mHasWifiEntries;
@VisibleForTesting
static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@@ -174,6 +177,8 @@
protected KeyguardStateController mKeyguardStateController;
@VisibleForTesting
protected boolean mHasEthernet = false;
+ @VisibleForTesting
+ protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -234,6 +239,7 @@
mSignalDrawable = new SignalDrawable(mContext);
mLocationController = locationController;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
}
void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -274,6 +280,7 @@
mAccessPointController.removeAccessPointCallback(this);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
+ mConnectedWifiInternetMonitor.unregisterCallback();
}
@VisibleForTesting
@@ -281,6 +288,10 @@
return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
}
+ void setAirplaneModeDisabled() {
+ mConnectivityManager.setAirplaneMode(false);
+ }
+
@VisibleForTesting
protected int getDefaultDataSubscriptionId() {
return mSubscriptionManager.getDefaultDataSubscriptionId();
@@ -309,15 +320,11 @@
}
CharSequence getSubtitleText(boolean isProgressBarVisible) {
- if (isAirplaneModeEnabled()) {
- return null;
- }
-
if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
- // When the airplane mode is off and Wi-Fi is disabled.
+ // When Wi-Fi is disabled.
// Sub-Title: Wi-Fi is off
if (DEBUG) {
- Log.d(TAG, "Airplane mode off + Wi-Fi off.");
+ Log.d(TAG, "Wi-Fi off.");
}
return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
}
@@ -331,7 +338,7 @@
return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS);
}
- if (mConnectedEntry != null || mWifiEntriesCount > 0) {
+ if (mHasWifiEntries) {
return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null;
}
@@ -341,6 +348,10 @@
return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
}
+ if (isCarrierNetworkActive()) {
+ return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
// Sub-Title:
// show non_carrier_network_unavailable
// - while Wi-Fi on + no Wi-Fi item
@@ -352,7 +363,7 @@
if (DEBUG) {
Log.d(TAG, "No Wi-Fi item.");
}
- if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) {
+ if (!hasActiveSubId() || (!isVoiceStateInService() && !isDataStateInService())) {
if (DEBUG) {
Log.d(TAG, "No carrier or service is out of service.");
}
@@ -403,15 +414,16 @@
return drawable;
}
- if (isDataStateInService() || isVoiceStateInService()) {
+ boolean isCarrierNetworkActive = isCarrierNetworkActive();
+ if (isDataStateInService() || isVoiceStateInService() || isCarrierNetworkActive) {
AtomicReference<Drawable> shared = new AtomicReference<>();
- shared.set(getSignalStrengthDrawableWithLevel());
+ shared.set(getSignalStrengthDrawableWithLevel(isCarrierNetworkActive));
drawable = shared.get();
}
int tintColor = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.textColorTertiary);
- if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
+ if (activeNetworkIsCellular() || isCarrierNetworkActive) {
tintColor = mContext.getColor(R.color.connected_network_primary_color);
}
drawable.setTint(tintColor);
@@ -426,12 +438,15 @@
*
* @return The Drawable which is a signal bar icon with level.
*/
- Drawable getSignalStrengthDrawableWithLevel() {
+ Drawable getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive) {
final SignalStrength strength = mTelephonyManager.getSignalStrength();
int level = (strength == null) ? 0 : strength.getLevel();
int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
- if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
- level += 1;
+ if ((mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId))
+ || isCarrierNetworkActive) {
+ level = isCarrierNetworkActive
+ ? SignalStrength.NUM_SIGNAL_STRENGTH_BINS
+ : (level + 1);
numLevels += 1;
}
return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
@@ -586,15 +601,18 @@
if (!isMobileDataEnabled()) {
return context.getString(R.string.mobile_data_off_summary);
}
- if (!isDataStateInService()) {
- return context.getString(R.string.mobile_data_no_connection);
- }
+
String summary = networkTypeDescription;
+ // Set network description for the carrier network when connecting to the carrier network
+ // under the airplane mode ON.
if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
summary = context.getString(R.string.preference_summary_default_combination,
context.getString(R.string.mobile_data_connection_active),
networkTypeDescription);
+ } else if (!isDataStateInService()) {
+ summary = context.getString(R.string.mobile_data_no_connection);
}
+
return summary;
}
@@ -678,7 +696,7 @@
/**
* @return whether there is the carrier item in the slice.
*/
- boolean hasCarrier() {
+ boolean hasActiveSubId() {
if (mSubscriptionManager == null) {
if (DEBUG) {
Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
@@ -861,59 +879,36 @@
return;
}
- if (accessPoints == null || accessPoints.size() == 0) {
- mConnectedEntry = null;
- mWifiEntriesCount = 0;
- if (mCallback != null) {
- mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */);
+ WifiEntry connectedEntry = null;
+ List<WifiEntry> wifiEntries = null;
+ final int accessPointsSize = (accessPoints == null) ? 0 : accessPoints.size();
+ final boolean hasMoreWifiEntries = (accessPointsSize > MAX_WIFI_ENTRY_COUNT);
+ if (accessPointsSize > 0) {
+ wifiEntries = new ArrayList<>();
+ final int count = hasMoreWifiEntries ? MAX_WIFI_ENTRY_COUNT : accessPointsSize;
+ mConnectedWifiInternetMonitor.unregisterCallback();
+ for (int i = 0; i < count; i++) {
+ WifiEntry entry = accessPoints.get(i);
+ mConnectedWifiInternetMonitor.registerCallbackIfNeed(entry);
+ if (connectedEntry == null && entry.isDefaultNetwork()
+ && entry.hasInternetAccess()) {
+ connectedEntry = entry;
+ } else {
+ wifiEntries.add(entry);
+ }
}
- return;
+ mHasWifiEntries = true;
+ } else {
+ mHasWifiEntries = false;
}
- boolean hasConnectedWifi = false;
- final int accessPointSize = accessPoints.size();
- for (int i = 0; i < accessPointSize; i++) {
- WifiEntry wifiEntry = accessPoints.get(i);
- if (wifiEntry.isDefaultNetwork() && wifiEntry.hasInternetAccess()) {
- mConnectedEntry = wifiEntry;
- hasConnectedWifi = true;
- break;
- }
- }
- if (!hasConnectedWifi) {
- mConnectedEntry = null;
- }
-
- int count = MAX_WIFI_ENTRY_COUNT;
- if (mHasEthernet) {
- count -= 1;
- }
- if (hasCarrier()) {
- count -= 1;
- }
- if (hasConnectedWifi) {
- count -= 1;
- }
- final List<WifiEntry> wifiEntries = accessPoints.stream()
- .filter(wifiEntry -> (!wifiEntry.isDefaultNetwork()
- || !wifiEntry.hasInternetAccess()))
- .limit(count)
- .collect(Collectors.toList());
- mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
-
- if (mCallback != null) {
- mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry);
- }
+ mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries);
}
@Override
public void onSettingsActivityTriggered(Intent settingsIntent) {
}
- @Override
- public void onDisconnectResult(int status) {
- }
-
private class InternetTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DisplayInfoListener,
@@ -983,6 +978,55 @@
}
/**
+ * Helper class for monitoring the Internet access of the connected WifiEntry.
+ */
+ @VisibleForTesting
+ protected class ConnectedWifiInternetMonitor implements WifiEntry.WifiEntryCallback {
+
+ private WifiEntry mWifiEntry;
+
+ public void registerCallbackIfNeed(WifiEntry entry) {
+ if (entry == null || mWifiEntry != null) {
+ return;
+ }
+ // If the Wi-Fi is not connected yet, or it's the connected Wi-Fi with Internet
+ // access. Then we don't need to listen to the callback to update the Wi-Fi entries.
+ if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED
+ || (entry.isDefaultNetwork() && entry.hasInternetAccess())) {
+ return;
+ }
+ mWifiEntry = entry;
+ entry.setListener(this);
+ }
+
+ public void unregisterCallback() {
+ if (mWifiEntry == null) {
+ return;
+ }
+ mWifiEntry.setListener(null);
+ mWifiEntry = null;
+ }
+
+ @MainThread
+ @Override
+ public void onUpdated() {
+ if (mWifiEntry == null) {
+ return;
+ }
+ WifiEntry entry = mWifiEntry;
+ if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED) {
+ unregisterCallback();
+ return;
+ }
+ if (entry.isDefaultNetwork() && entry.hasInternetAccess()) {
+ unregisterCallback();
+ // Trigger onAccessPointsChanged() to update the Wi-Fi entries.
+ scanWifiAccessPoints();
+ }
+ }
+ }
+
+ /**
* Return {@code true} If the Ethernet exists
*/
@MainThread
@@ -1057,7 +1101,7 @@
void dismissDialog();
void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
- @Nullable WifiEntry connectedEntry);
+ @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
}
void makeOverlayToast(int stringId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
deleted file mode 100644
index 26d1bbd..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.user
-
-import android.content.Context
-import android.os.Bundle
-import android.view.Gravity
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowInsets
-import android.view.WindowManager
-import com.android.systemui.qs.PseudoGridView
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.R
-
-/**
- * Dialog for switching users or creating new ones.
- */
-class UserDialog(
- context: Context
-) : SystemUIDialog(context) {
-
- // create() is no-op after creation
- private lateinit var _doneButton: View
- /**
- * Button with text "Done" in dialog.
- */
- val doneButton: View
- get() {
- create()
- return _doneButton
- }
-
- private lateinit var _settingsButton: View
- /**
- * Button with text "User Settings" in dialog.
- */
- val settingsButton: View
- get() {
- create()
- return _settingsButton
- }
-
- private lateinit var _grid: PseudoGridView
- /**
- * Grid to populate with user avatar from adapter
- */
- val grid: ViewGroup
- get() {
- create()
- return _grid
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- window?.apply {
- setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
- attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
- attributes.receiveInsetsIgnoringZOrder = true
- setGravity(Gravity.CENTER)
- }
- setContentView(R.layout.qs_user_dialog_content)
-
- _doneButton = requireViewById(R.id.done)
- _settingsButton = requireViewById(R.id.settings)
- _grid = requireViewById(R.id.grid)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index d74a50e..7c8f4b15 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -21,13 +21,16 @@
import android.content.DialogInterface
import android.content.Intent
import android.provider.Settings
+import android.view.LayoutInflater
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
import javax.inject.Provider
@@ -40,7 +43,7 @@
private val activityStarter: ActivityStarter,
private val falsingManager: FalsingManager,
private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val dialogFactory: (Context) -> UserDialog
+ private val dialogFactory: (Context) -> SystemUIDialog
) {
@Inject
@@ -54,7 +57,7 @@
activityStarter,
falsingManager,
dialogLaunchAnimator,
- { UserDialog(it) }
+ { SystemUIDialog(it) }
)
companion object {
@@ -71,9 +74,10 @@
with(dialogFactory(view.context)) {
setShowForAllUsers(true)
setCanceledOnTouchOutside(true)
- create() // Needs to be called before we can retrieve views
- settingsButton.setOnClickListener {
+ setTitle(R.string.qs_user_switch_dialog_title)
+ setPositiveButton(R.string.quick_settings_done, null)
+ setNeutralButton(R.string.quick_settings_more_user_settings) { _, _ ->
if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
activityStarter.postStartActivityDismissingKeyguard(
@@ -81,31 +85,33 @@
0
)
}
- dismiss()
}
- doneButton.setOnClickListener { dismiss() }
+ val gridFrame = LayoutInflater.from(this.context)
+ .inflate(R.layout.qs_user_dialog_content, null)
+ setView(gridFrame)
val adapter = userDetailViewAdapterProvider.get()
- adapter.linkToViewGroup(grid)
- val hostDialog = dialogLaunchAnimator.showFromView(this, view)
- adapter.injectDialogShower(DialogShowerImpl(hostDialog, dialogLaunchAnimator))
+ adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
+
+ dialogLaunchAnimator.showFromView(this, view)
+ adapter.injectDialogShower(DialogShowerImpl(this, dialogLaunchAnimator))
}
}
private class DialogShowerImpl(
- private val hostDialog: Dialog,
+ private val animateFrom: Dialog,
private val dialogLaunchAnimator: DialogLaunchAnimator
- ) : DialogInterface by hostDialog, DialogShower {
- override fun showDialog(dialog: Dialog): Dialog {
- return dialogLaunchAnimator.showFromDialog(
+ ) : DialogInterface by animateFrom, DialogShower {
+ override fun showDialog(dialog: Dialog) {
+ dialogLaunchAnimator.showFromDialog(
dialog,
- parentHostDialog = hostDialog
+ animateFrom = animateFrom
)
}
}
interface DialogShower : DialogInterface {
- fun showDialog(dialog: Dialog): Dialog
+ fun showDialog(dialog: Dialog)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index fa874b1..e7cd1e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -76,6 +76,7 @@
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
@@ -88,6 +89,7 @@
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -161,6 +163,7 @@
private final Optional<StartingSurface> mStartingSurface;
private final SmartspaceTransitionController mSmartspaceTransitionController;
private final Optional<RecentTasks> mRecentTasks;
+ private final UiEventLogger mUiEventLogger;
private Region mActiveNavBarRegion;
@@ -248,6 +251,7 @@
mContext.getSystemService(InputMethodManager.class)
.showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
DEFAULT_DISPLAY);
+ mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
}
@Override
@@ -560,6 +564,7 @@
ShellTransitions shellTransitions,
ScreenLifecycle screenLifecycle,
SmartspaceTransitionController smartspaceTransitionController,
+ UiEventLogger uiEventLogger,
DumpManager dumpManager) {
super(broadcastDispatcher);
mContext = context;
@@ -581,6 +586,7 @@
mOneHandedOptional = oneHandedOptional;
mShellTransitions = shellTransitions;
mRecentTasks = recentTasks;
+ mUiEventLogger = uiEventLogger;
// Assumes device always starts with back button until launcher tells it that it does not
mNavBarButtonAlpha = 1.0f;
@@ -986,6 +992,18 @@
}
}
+ public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy to update nav buttons dark intensity");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onNavButtonsDarkIntensityChanged()", e);
+ }
+ }
+
private void updateEnabledState() {
final int currentUser = ActivityManagerWrapper.getInstance().getCurrentUserId();
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 85bf98c..7f130cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -248,8 +249,8 @@
.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
WindowManagerWrapper wm = WindowManagerWrapper.getInstance();
- if (!QuickStepContract.isGesturalMode(mNavBarMode)
- && wm.hasSoftNavigationBar(mContext.getDisplayId())) {
+ if (!QuickStepContract.isGesturalMode(mNavBarMode)
+ && wm.hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
swapChildrenIfRtlAndVertical(buttons);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 5bb3413..d64c05f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -32,7 +32,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
@@ -62,7 +61,6 @@
private static final String EXTRA_RESULT_CODE = "extra_resultCode";
private static final String EXTRA_PATH = "extra_path";
private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
- private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
private static final String ACTION_START = "com.android.systemui.screenrecord.START";
private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
@@ -74,8 +72,6 @@
private final RecordingController mController;
private final KeyguardDismissUtil mKeyguardDismissUtil;
private ScreenRecordingAudioSource mAudioSource;
- private boolean mShowTaps;
- private boolean mOriginalShowTaps;
private ScreenMediaRecorder mRecorder;
private final Executor mLongExecutor;
private final UiEventLogger mUiEventLogger;
@@ -102,15 +98,12 @@
* android.content.Intent)}
* @param audioSource The ordinal value of the audio source
* {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource}
- * @param showTaps True to make touches visible while recording
*/
- public static Intent getStartIntent(Context context, int resultCode,
- int audioSource, boolean showTaps) {
+ public static Intent getStartIntent(Context context, int resultCode, int audioSource) {
return new Intent(context, RecordingService.class)
.setAction(ACTION_START)
.putExtra(EXTRA_RESULT_CODE, resultCode)
- .putExtra(EXTRA_AUDIO_SOURCE, audioSource)
- .putExtra(EXTRA_SHOW_TAPS, showTaps);
+ .putExtra(EXTRA_AUDIO_SOURCE, audioSource);
}
@Override
@@ -128,13 +121,6 @@
mAudioSource = ScreenRecordingAudioSource
.values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)];
Log.d(TAG, "recording with audio source" + mAudioSource);
- mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);
-
- mOriginalShowTaps = Settings.System.getInt(
- getApplicationContext().getContentResolver(),
- Settings.System.SHOW_TOUCHES, 0) != 0;
-
- setTapsVisible(mShowTaps);
mRecorder = new ScreenMediaRecorder(
mUserContextTracker.getUserContext(),
@@ -379,7 +365,6 @@
}
private void stopRecording(int userId) {
- setTapsVisible(mOriginalShowTaps);
if (getRecorder() != null) {
getRecorder().end();
saveRecording(userId);
@@ -411,11 +396,6 @@
});
}
- private void setTapsVisible(boolean turnOn) {
- int value = turnOn ? 1 : 0;
- Settings.System.putInt(getContentResolver(), Settings.System.SHOW_TOUCHES, value);
- }
-
/**
* Get an intent to stop the recording service.
* @param context Context from the requesting activity
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 1fb88df..582cc76 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -55,7 +55,6 @@
private final UserContextProvider mUserContextProvider;
@Nullable
private final Runnable mOnStartRecordingClicked;
- private Switch mTapsSwitch;
private Switch mAudioSwitch;
private Spinner mOptions;
@@ -96,7 +95,6 @@
});
mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
- mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
mOptions = findViewById(R.id.screen_recording_options);
ArrayAdapter a = new ScreenRecordingAdapter(getContext().getApplicationContext(),
android.R.layout.simple_spinner_dropdown_item,
@@ -110,7 +108,6 @@
private void requestScreenCapture() {
Context userContext = mUserContextProvider.getUserContext();
- boolean showTaps = mTapsSwitch.isChecked();
ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
: NONE;
@@ -118,7 +115,7 @@
RecordingService.REQUEST_CODE,
RecordingService.getStartIntent(
userContext, Activity.RESULT_OK,
- audioMode.ordinal(), showTaps),
+ audioMode.ordinal()),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
PendingIntent stopIntent = PendingIntent.getService(userContext,
RecordingService.REQUEST_CODE,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 44b4540..e368577 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -39,11 +39,13 @@
import android.app.ActivityOptions;
import android.app.ExitTransitionCoordinator;
import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
+import android.app.ICompatCameraControlCallback;
import android.app.Notification;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -72,6 +74,7 @@
import android.view.ScrollCaptureResponse;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
@@ -597,20 +600,35 @@
withWindowAttached(() -> {
requestScrollCapture();
mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
- (overrideConfig, newDisplayId) -> {
- if (mConfigChanges.applyNewConfig(mContext.getResources())) {
- // Hide the scroll chip until we know it's available in this orientation
- mScreenshotView.hideScrollChip();
- // Delay scroll capture eval a bit to allow the underlying activity
- // to set up in the new orientation.
- mScreenshotHandler.postDelayed(this::requestScrollCapture, 150);
- mScreenshotView.updateInsets(
- mWindowManager.getCurrentWindowMetrics().getWindowInsets());
- // screenshot animation calculations won't be valid anymore, so just end
- if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
- mScreenshotAnimation.end();
+ new ViewRootImpl.ActivityConfigCallback() {
+ @Override
+ public void onConfigurationChanged(Configuration overrideConfig,
+ int newDisplayId) {
+ if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+ // Hide the scroll chip until we know it's available in this
+ // orientation
+ mScreenshotView.hideScrollChip();
+ // Delay scroll capture eval a bit to allow the underlying activity
+ // to set up in the new orientation.
+ mScreenshotHandler.postDelayed(
+ ScreenshotController.this::requestScrollCapture, 150);
+ mScreenshotView.updateInsets(
+ mWindowManager.getCurrentWindowMetrics()
+ .getWindowInsets());
+ // Screenshot animation calculations won't be valid anymore,
+ // so just end
+ if (mScreenshotAnimation != null
+ && mScreenshotAnimation.isRunning()) {
+ mScreenshotAnimation.end();
+ }
}
}
+ @Override
+ public void requestCompatCameraControl(boolean showControl,
+ boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ Log.w(TAG, "Unexpected requestCompatCameraControl callback");
+ }
});
});
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
index c50365f..71c5fad 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
@@ -15,7 +15,8 @@
class SensorUseDialog(
context: Context,
val sensor: Int,
- val clickListener: DialogInterface.OnClickListener
+ val clickListener: DialogInterface.OnClickListener,
+ val dismissListener: DialogInterface.OnDismissListener
) : SystemUIDialog(context) {
// TODO move to onCreate (b/200815309)
@@ -69,6 +70,8 @@
context.getString(com.android.internal.R.string
.cancel), clickListener)
+ setOnDismissListener(dismissListener)
+
setCancelable(false)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index b0071d9..dae375a 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -50,7 +50,7 @@
private val keyguardStateController: KeyguardStateController,
private val keyguardDismissUtil: KeyguardDismissUtil,
@Background private val bgHandler: Handler
-) : Activity(), DialogInterface.OnClickListener {
+) : Activity(), DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
companion object {
private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
@@ -120,7 +120,7 @@
}
}
- mDialog = SensorUseDialog(this, sensor, this)
+ mDialog = SensorUseDialog(this, sensor, this, this)
mDialog!!.show()
}
@@ -212,4 +212,8 @@
.suppressSensorPrivacyReminders(sensor, suppressed)
}
}
+
+ override fun onDismiss(dialog: DialogInterface?) {
+ finish()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6676901..5876703 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -513,9 +513,13 @@
* @param animate {@code true} to show animations.
*/
public void recomputeDisableFlags(int displayId, boolean animate) {
- int disabled1 = getDisabled1(displayId);
- int disabled2 = getDisabled2(displayId);
- disable(displayId, disabled1, disabled2, animate);
+ // This must update holding the lock otherwise it can clobber the disabled flags set on the
+ // binder thread from the disable() call
+ synchronized (mLock) {
+ int disabled1 = getDisabled1(displayId);
+ int disabled2 = getDisabled2(displayId);
+ disable(displayId, disabled1, disabled2, animate);
+ }
}
private void setDisabled(int displayId, int disabled1, int disabled2) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 190c773e..d7b4738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -21,8 +21,10 @@
import static android.view.View.VISIBLE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
@@ -94,6 +96,15 @@
/**
* Controls the indications and error messages shown on the Keyguard
+ *
+ * On AoD, only one message shows with the following priorities:
+ * 1. Biometric
+ * 2. Transient
+ * 3. Charging alignment
+ * 4. Battery information
+ *
+ * On the lock screen, message rotate through different message types.
+ * See {@link KeyguardIndicationRotateTextViewController.IndicationType} for the list of types.
*/
@SysUISingleton
public class KeyguardIndicationController {
@@ -103,6 +114,7 @@
private static final int MSG_HIDE_TRANSIENT = 1;
private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2;
+ private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3;
private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
private static final float BOUNCE_ANIMATION_FINAL_Y = 0f;
@@ -132,9 +144,9 @@
private String mRestingIndication;
private String mAlignmentIndication;
private CharSequence mTransientIndication;
+ private CharSequence mBiometricMessage;
protected ColorStateList mInitialTextColorState;
private boolean mVisible;
- private boolean mHideTransientMessageOnScreenOff;
private boolean mPowerPluggedIn;
private boolean mPowerPluggedInWired;
@@ -277,13 +289,15 @@
}
/**
- * Doesn't include disclosure which gets triggered separately.
+ * Doesn't include disclosure (also a persistent indication) which gets triggered separately.
+ *
+ * This method also doesn't update transient messages like biometrics since those messages
+ * are also updated separately.
*/
- private void updateIndications(boolean animate, int userId) {
+ private void updatePersistentIndications(boolean animate, int userId) {
updateOwnerInfo();
updateBattery(animate);
updateUserLocked(userId);
- updateTransient();
updateTrust(userId, getTrustGrantedIndication(), getTrustManagedIndication());
updateAlignment();
updateLogoutView();
@@ -383,12 +397,36 @@
}
}
+ private void updateBiometricMessage() {
+ if (!TextUtils.isEmpty(mBiometricMessage)) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ new KeyguardIndication.Builder()
+ .setMessage(mBiometricMessage)
+ .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ true
+ );
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ if (mDozing) {
+ updateIndication(false);
+ }
+ }
+
private void updateTransient() {
if (!TextUtils.isEmpty(mTransientIndication)) {
mRotateTextViewController.showTransient(mTransientIndication);
} else {
mRotateTextViewController.hideTransient();
}
+
+ if (mDozing) {
+ updateIndication(false);
+ }
}
private void updateTrust(int userId, CharSequence trustGrantedIndication,
@@ -577,6 +615,14 @@
}
/**
+ * Hides biometric indication in {@param delayMs}.
+ */
+ public void hideBiometricMessageDelayed(long delayMs) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_HIDE_BIOMETRIC_MESSAGE), delayMs);
+ }
+
+ /**
* Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
*/
public void showTransientIndication(int transientIndication) {
@@ -586,23 +632,40 @@
/**
* Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
*/
- public void showTransientIndication(CharSequence transientIndication) {
- showTransientIndication(transientIndication, false /* isError */,
- false /* hideOnScreenOff */);
+ private void showTransientIndication(CharSequence transientIndication) {
+ mTransientIndication = transientIndication;
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+
+ updateTransient();
}
/**
- * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
+ * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}.
*/
- private void showTransientIndication(CharSequence transientIndication,
- boolean isError, boolean hideOnScreenOff) {
- mTransientIndication = transientIndication;
- mHideTransientMessageOnScreenOff = hideOnScreenOff && transientIndication != null;
- mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
- hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+ public void showBiometricMessage(int biometricMessage) {
+ showBiometricMessage(mContext.getResources().getString(biometricMessage));
+ }
- updateIndication(false);
+ /**
+ * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}.
+ */
+ private void showBiometricMessage(CharSequence biometricMessage) {
+ mBiometricMessage = biometricMessage;
+
+ mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
+ mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
+ hideBiometricMessageDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+
+ updateBiometricMessage();
+ }
+
+ private void hideBiometricMessage() {
+ if (mBiometricMessage != null) {
+ mBiometricMessage = null;
+ mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
+ updateBiometricMessage();
+ }
}
/**
@@ -611,10 +674,8 @@
public void hideTransientIndication() {
if (mTransientIndication != null) {
mTransientIndication = null;
- mHideTransientMessageOnScreenOff = false;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- mRotateTextViewController.hideTransient();
- updateIndication(false);
+ updateTransient();
}
}
@@ -635,7 +696,11 @@
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
mTopIndicationView.setTextColor(Color.WHITE);
- if (!TextUtils.isEmpty(mTransientIndication)) {
+ if (!TextUtils.isEmpty(mBiometricMessage)) {
+ mWakeLock.setAcquired(true);
+ mTopIndicationView.switchIndication(mBiometricMessage, null,
+ true, () -> mWakeLock.setAcquired(false));
+ } else if (!TextUtils.isEmpty(mTransientIndication)) {
mWakeLock.setAcquired(true);
mTopIndicationView.switchIndication(mTransientIndication, null,
true, () -> mWakeLock.setAcquired(false));
@@ -669,7 +734,7 @@
mTopIndicationView.setVisibility(GONE);
mTopIndicationView.setText(null);
mLockScreenIndicationView.setVisibility(View.VISIBLE);
- updateIndications(animate, KeyguardUpdateMonitor.getCurrentUser());
+ updatePersistentIndications(animate, KeyguardUpdateMonitor.getCurrentUser());
}
// animates textView - textView moves up and bounces down
@@ -717,6 +782,9 @@
textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
ViewClippingUtil.setClippingDeactivated(textView, false,
mClippingParams);
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ textView.animate().setListener(null);
}
});
}
@@ -795,6 +863,8 @@
hideTransientIndication();
} else if (msg.what == MSG_SHOW_ACTION_TO_UNLOCK) {
showActionToUnlock();
+ } else if (msg.what == MSG_HIDE_BIOMETRIC_MESSAGE) {
+ hideBiometricMessage();
}
}
};
@@ -817,8 +887,13 @@
mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
}
} else {
- showTransientIndication(mContext.getString(R.string.keyguard_unlock),
- false /* isError */, true /* hideOnScreenOff */);
+ if (mKeyguardUpdateMonitor.isUdfpsSupported()
+ && mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock_press));
+ } else {
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ }
}
}
@@ -827,15 +902,15 @@
// if udfps available, there will always be a tappable affordance to unlock
// For example, the lock icon
if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
- showTransientIndication(R.string.keyguard_unlock_press);
+ showBiometricMessage(R.string.keyguard_unlock_press);
} else if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
// since face is locked out, simply show "try fingerprint"
- showTransientIndication(R.string.keyguard_try_fingerprint);
+ showBiometricMessage(R.string.keyguard_try_fingerprint);
} else {
- showTransientIndication(R.string.keyguard_face_failed_use_fp);
+ showBiometricMessage(R.string.keyguard_face_failed_use_fp);
}
} else {
- showTransientIndication(R.string.keyguard_try_fingerprint);
+ showBiometricMessage(R.string.keyguard_try_fingerprint);
}
// Although we suppress face auth errors visually, we still announce them for a11y
@@ -854,6 +929,8 @@
pw.println(" mChargingWattage: " + mChargingWattage);
pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
pw.println(" mDozing: " + mDozing);
+ pw.println(" mTransientIndication: " + mTransientIndication);
+ pw.println(" mBiometricMessage: " + mBiometricMessage);
pw.println(" mBatteryLevel: " + mBatteryLevel);
pw.println(" mBatteryPresent: " + mBatteryPresent);
pw.println(" mTextView.getText(): " + (
@@ -868,7 +945,7 @@
@Override
public void onRefreshBatteryInfo(BatteryStatus status) {
boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
- || status.status == BatteryManager.BATTERY_STATUS_FULL;
+ || status.isCharged();
boolean wasPluggedIn = mPowerPluggedIn;
mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
mPowerPluggedInWireless = status.isPluggedInWireless() && isChargingOrFull;
@@ -909,7 +986,6 @@
.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) {
return;
}
-
boolean showActionToUnlock =
msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
@@ -918,14 +994,10 @@
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
if (biometricSourceType == BiometricSourceType.FACE
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
- // don't show any help messages, b/c they can come in right before a success
- // However, continue to announce help messages for a11y
- if (!TextUtils.isEmpty(helpString)) {
- mLockScreenIndicationView.announceForAccessibility(helpString);
- }
+ showTryFingerprintMsg(msgId, helpString);
return;
}
- showTransientIndication(helpString, false /* isError */, showActionToUnlock);
+ showBiometricMessage(helpString);
} else if (showActionToUnlock) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
@@ -964,8 +1036,7 @@
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
- showTransientIndication(errString, /* isError */ true,
- /* hideOnScreenOff */ true);
+ showBiometricMessage(errString);
} else {
mMessageToShowOnScreenOn = errString;
}
@@ -1011,16 +1082,15 @@
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
- showTransientIndication(message, true /* isError */, false /* hideOnScreenOff */);
+ showBiometricMessage(message);
}
@Override
public void onScreenTurnedOn() {
if (mMessageToShowOnScreenOn != null) {
- showTransientIndication(mMessageToShowOnScreenOn, true /* isError */,
- false /* hideOnScreenOff */);
+ showBiometricMessage(mMessageToShowOnScreenOn);
// We want to keep this message around in case the screen was off
- hideTransientIndicationDelayed(HIDE_DELAY_MS);
+ hideBiometricMessageDelayed(HIDE_DELAY_MS);
mMessageToShowOnScreenOn = null;
}
}
@@ -1031,7 +1101,7 @@
if (running && biometricSourceType == BiometricSourceType.FACE) {
// Let's hide any previous messages when authentication starts, otherwise
// multiple auth attempts would overlap.
- hideTransientIndication();
+ hideBiometricMessage();
mMessageToShowOnScreenOn = null;
}
}
@@ -1040,11 +1110,11 @@
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric);
- mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT);
+ hideBiometricMessage();
if (biometricSourceType == BiometricSourceType.FACE
&& !mKeyguardBypassController.canBypass()) {
- mHandler.sendEmptyMessage(MSG_SHOW_ACTION_TO_UNLOCK);
+ showActionToUnlock();
}
}
@@ -1071,8 +1141,7 @@
@Override
public void onRequireUnlockForNfc() {
- showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc),
- false /* isError */, false /* hideOnScreenOff */);
+ showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc));
hideTransientIndicationDelayed(HIDE_DELAY_MS);
}
}
@@ -1091,8 +1160,8 @@
}
mDozing = dozing;
- if (mHideTransientMessageOnScreenOff && mDozing) {
- hideTransientIndication();
+ if (mDozing) {
+ hideBiometricMessage();
}
updateIndication(false);
}
@@ -1109,7 +1178,7 @@
public void onKeyguardShowingChanged() {
if (!mKeyguardStateController.isShowing()) {
mTopIndicationView.clearMessages();
- mLockScreenIndicationView.clearMessages();
+ mRotateTextViewController.clearMessages();
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index dca7f70..0fb08e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -174,7 +174,7 @@
internal fun canDragDown(): Boolean {
return (statusBarStateController.state == StatusBarState.KEYGUARD ||
nsslController.isInLockedDownShade()) &&
- qS.isFullyCollapsed
+ (qS.isFullyCollapsed || useSplitShade)
}
/**
@@ -285,7 +285,7 @@
internal val isDragDownAnywhereEnabled: Boolean
get() = (statusBarStateController.getState() == StatusBarState.KEYGUARD &&
!keyguardBypassController.bypassEnabled &&
- qS.isFullyCollapsed)
+ (qS.isFullyCollapsed || useSplitShade))
/**
* The amount in pixels that the user has dragged down.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 5098370..0e3b5b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -242,12 +242,13 @@
@Override
public void onMediaDataLoaded(@NonNull String key,
@Nullable String oldKey, @NonNull MediaData data, boolean immediately,
- boolean isSsReactivated) {
+ int receivedSmartspaceCardLatency) {
}
@Override
public void onSmartspaceMediaDataLoaded(@NonNull String key,
- @NonNull SmartspaceMediaData data, boolean shouldPrioritize) {
+ @NonNull SmartspaceMediaData data, boolean shouldPrioritize,
+ boolean isSsReactivated) {
}
@Override
@@ -316,12 +317,13 @@
@Override
public void onMediaDataLoaded(@NonNull String key,
@Nullable String oldKey, @NonNull MediaData data, boolean immediately,
- boolean isSsReactivated) {
+ int receivedSmartspaceCardLatency) {
}
@Override
public void onSmartspaceMediaDataLoaded(@NonNull String key,
- @NonNull SmartspaceMediaData data, boolean shouldPrioritize) {
+ @NonNull SmartspaceMediaData data, boolean shouldPrioritize,
+ boolean isSsReactivated) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
index c4fadff..4551807 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
@@ -40,7 +40,7 @@
super(context);
setTitle(R.string.user_remove_user_title);
setMessage(context.getString(R.string.user_remove_user_message));
- setButton(DialogInterface.BUTTON_NEGATIVE,
+ setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(R.string.user_remove_user_remove), this);
@@ -51,7 +51,7 @@
@Override
public void onClick(DialogInterface dialog, int which) {
- if (which == BUTTON_NEGATIVE) {
+ if (which == BUTTON_NEUTRAL) {
cancel();
} else {
dismiss();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
new file mode 100644
index 0000000..73d3e2a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.systemui.statusbar.charging
+
+import android.graphics.Color
+import android.graphics.PointF
+import android.graphics.RuntimeShader
+import android.util.MathUtils
+
+/**
+ * Shader class that renders a distorted ripple for the UDFPS dwell effect.
+ * Adjustable shader parameters:
+ * - progress
+ * - origin
+ * - color
+ * - time
+ * - maxRadius
+ * - distortionStrength.
+ * See per field documentation for more details.
+ *
+ * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
+ */
+class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
+ companion object {
+ private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
+ uniform float in_time;
+ uniform float in_radius;
+ uniform float in_blur;
+ uniform vec4 in_color;
+ uniform float in_phase1;
+ uniform float in_phase2;
+ uniform float in_distortion_strength;"""
+ private const val SHADER_LIB = """
+ float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
+ float blurHalf = blur * 0.5;
+ float d = distance(uv, xy);
+ return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
+ }
+
+ float softRing(vec2 uv, vec2 xy, float radius, float blur) {
+ float thickness_half = radius * 0.25;
+ float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
+ float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
+ return circle_outer - circle_inner;
+ }
+
+ vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) {
+ return p + vec2(sin(p.y * frequency + in_phase1),
+ cos(p.x * frequency * -1.23 + in_phase2)) * distort_amount_xy;
+ }
+
+ vec4 ripple(vec2 p, float distort_xy, float frequency) {
+ vec2 p_distorted = distort(p, in_time, distort_xy, frequency);
+ float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
+ float rippleAlpha = max(circle,
+ softRing(p_distorted, in_origin, in_radius, in_blur)) * 0.25;
+ return in_color * rippleAlpha;
+ }
+ """
+ private const val SHADER_MAIN = """vec4 main(vec2 p) {
+ vec4 color1 = ripple(p,
+ 34 * in_distortion_strength, // distort_xy
+ 0.012 // frequency
+ );
+ vec4 color2 = ripple(p,
+ 49 * in_distortion_strength, // distort_xy
+ 0.018 // frequency
+ );
+ // Alpha blend between two layers.
+ return vec4(color1.xyz + color2.xyz
+ * (1 - color1.w), color1.w + color2.w * (1-color1.w));
+ }"""
+ private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
+ }
+
+ /**
+ * Maximum radius of the ripple.
+ */
+ var maxRadius: Float = 0.0f
+
+ /**
+ * Origin coordinate of the ripple.
+ */
+ var origin: PointF = PointF()
+ set(value) {
+ field = value
+ setUniform("in_origin", floatArrayOf(value.x, value.y))
+ }
+
+ /**
+ * Progress of the ripple. Float value between [0, 1].
+ */
+ var progress: Float = 0.0f
+ set(value) {
+ field = value
+ setUniform("in_radius",
+ (1 - (1 - value) * (1 - value) * (1 - value))* maxRadius)
+ setUniform("in_blur", MathUtils.lerp(1f, 0.7f, value))
+ }
+
+ /**
+ * Distortion strength between [0, 1], with 0 being no distortion and 1 being full distortion.
+ */
+ var distortionStrength: Float = 0.0f
+ set(value) {
+ field = value
+ setUniform("in_distortion_strength", value)
+ }
+
+ /**
+ * Play time since the start of the effect in seconds.
+ */
+ var time: Float = 0.0f
+ set(value) {
+ field = value * 0.001f
+ setUniform("in_time", field)
+ setUniform("in_phase1", field * 3f + 0.367f)
+ setUniform("in_phase2", field * 7.2f * 1.531f)
+ }
+
+ /**
+ * A hex value representing the ripple color, in the format of ARGB
+ */
+ var color: Int = 0xffffff.toInt()
+ set(value) {
+ field = value
+ val color = Color.valueOf(value)
+ setUniform("in_color", floatArrayOf(color.red(),
+ color.green(), color.blue(), color.alpha()))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 04c60fc..8909e9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -93,9 +93,8 @@
nowPluggedIn: Boolean,
charging: Boolean
) {
- // Suppresses the ripple when it's disabled, or when the state change comes
- // from wireless charging.
- if (!rippleEnabled || batteryController.isPluggedInWireless) {
+ // Suppresses the ripple when the state change comes from wireless charging.
+ if (batteryController.isPluggedInWireless) {
return
}
val wasPluggedIn = pluggedIn
@@ -145,7 +144,7 @@
}
fun startRipple() {
- if (!rippleEnabled || rippleView.rippleInProgress || rippleView.parent != null) {
+ if (rippleView.rippleInProgress || rippleView.parent != null) {
// Skip if ripple is still playing, or not playing but already added the parent
// (which might happen just before the animation starts or right after
// the animation ends.)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 103ca0e..ff9d919 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -83,7 +83,7 @@
@Override
public void notifyListeners(SignalCallback callback) {
if (mCurrentState.isCarrierMerged) {
- if (mCurrentState.isDefault) {
+ if (mCurrentState.isDefault || !mNetworkController.isRadioOn()) {
notifyListenersForCarrierWifi(callback);
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 74ea19f..f2d926d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -20,11 +20,11 @@
import android.app.NotificationManager;
import android.content.Context;
import android.os.Handler;
+import android.service.dreams.IDreamManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
-import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -67,13 +67,10 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
-import com.android.systemui.statusbar.window.StatusBarWindowModule;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -303,24 +300,15 @@
*/
@Provides
@SysUISingleton
- static LaunchAnimator provideLaunchAnimator(Context context) {
- return new LaunchAnimator(context);
+ static ActivityLaunchAnimator provideActivityLaunchAnimator() {
+ return new ActivityLaunchAnimator();
}
/**
*/
@Provides
@SysUISingleton
- static ActivityLaunchAnimator provideActivityLaunchAnimator(LaunchAnimator launchAnimator) {
- return new ActivityLaunchAnimator(launchAnimator);
- }
-
- /**
- */
- @Provides
- @SysUISingleton
- static DialogLaunchAnimator provideDialogLaunchAnimator(Context context,
- LaunchAnimator launchAnimator) {
- return new DialogLaunchAnimator(context, launchAnimator, new SystemUIHostDialogProvider());
+ static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager) {
+ return new DialogLaunchAnimator(dreamManager);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 4e5bc8e..6c4051e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -240,6 +240,10 @@
configurationController.addCallback(configChangeListener)
statusBarStateController.addCallback(statusBarStateListener)
+ plugin.registerSmartspaceEventNotifier {
+ e -> session?.notifySmartspaceEvent(e)
+ }
+
reloadSmartspace()
}
@@ -265,6 +269,7 @@
statusBarStateController.removeCallback(statusBarStateListener)
session = null
+ plugin?.registerSmartspaceEventNotifier(null)
plugin?.onTargetsAvailable(emptyList())
Log.d(TAG, "Ending smartspace session for lockscreen")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
index 64a7305..349b191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -2,6 +2,7 @@
import android.util.MathUtils
import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.LaunchAnimator
import kotlin.math.min
@@ -55,6 +56,7 @@
}
fun getProgress(delay: Long, duration: Long): Float {
- return LaunchAnimator.getProgress(linearProgress, delay, duration)
+ return LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, linearProgress, delay,
+ duration)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java
new file mode 100644
index 0000000..8d2e3c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java
@@ -0,0 +1,70 @@
+/*
+ * 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.systemui.statusbar.notification;
+
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Used to let views that have an alpha not apply the HARDWARE layer type directly, and instead
+ * delegate that to specific children. This is useful if we want to fake not having overlapping
+ * rendering to avoid layer trashing, when fading out a view that is also changing.
+ */
+public interface NotificationFadeAware {
+ /**
+ * Calls {@link View#setLayerType} with {@link View#LAYER_TYPE_HARDWARE} if faded and
+ * {@link View#LAYER_TYPE_NONE} otherwise.
+ */
+ static void setLayerTypeForFaded(@Nullable View view, boolean faded) {
+ if (view != null) {
+ int newLayerType = faded ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE;
+ view.setLayerType(newLayerType, null);
+ }
+ }
+
+ /**
+ * Used like {@link View#setLayerType} with {@link View#LAYER_TYPE_HARDWARE} or
+ * {@link View#LAYER_TYPE_NONE} except that instead of necessarily affecting this view
+ * specifically, this may delegate the call to child views.
+ *
+ * When set to <code>true</code>, the view has two possible paths:
+ * 1. If a hardware layer is required to ensure correct appearance of this view, then
+ * set that layer type.
+ * 2. Otherwise, delegate this call to children, who might make that call for themselves.
+ *
+ * When set to <code>false</code>, the view should undo the above, typically by calling
+ * {@link View#setLayerType} with {@link View#LAYER_TYPE_NONE} on itself and children, and
+ * delegating to this method on children where implemented.
+ *
+ * When this delegates to {@link View#setLayerType} on this view or a subview, `null` will be
+ * passed for the `paint` argument of that call.
+ */
+ void setNotificationFaded(boolean faded);
+
+ /**
+ * Interface for the top level notification view that fades and optimizes that through deep
+ * awareness of individual components.
+ */
+ interface FadeOptimizedNotification extends NotificationFadeAware {
+ /** Top-level feature switch */
+ boolean FADE_LAYER_OPTIMIZATION_ENABLED = true;
+
+ /** Determine if the notification is currently faded. */
+ boolean isNotificationFaded();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 39b1ec4..57d8d84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -73,7 +73,6 @@
mCoordinators.add(bubbleCoordinator)
mCoordinators.add(conversationCoordinator)
mCoordinators.add(mediaCoordinator)
- mCoordinators.add(remoteInputCoordinator)
mCoordinators.add(shadeEventCoordinator)
mCoordinators.add(viewConfigCoordinator)
mCoordinators.add(visualStabilityCoordinator)
@@ -85,6 +84,7 @@
mCoordinators.add(headsUpCoordinator)
mCoordinators.add(gutsCoordinator)
mCoordinators.add(preparationCoordinator)
+ mCoordinators.add(remoteInputCoordinator)
}
// Manually add Ordered Sections
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 4441270..fa9217d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -20,6 +20,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
@@ -158,6 +159,9 @@
mExpansionStateLogger.onVisibilityChanged(
mTmpCurrentlyVisibleNotifications, mTmpCurrentlyVisibleNotifications);
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Active]", N);
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Visible]",
+ mCurrentlyVisibleNotifications.size());
recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications);
mTmpCurrentlyVisibleNotifications.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 2eb2065..63cb4ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -579,14 +579,24 @@
if (contentView.hasOverlappingRendering()) {
int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
: LAYER_TYPE_HARDWARE;
- int currentLayerType = contentView.getLayerType();
- if (currentLayerType != layerType) {
- contentView.setLayerType(layerType, null);
- }
+ contentView.setLayerType(layerType, null);
}
contentView.setAlpha(contentAlpha);
+ // After updating the current view, reset all views.
+ if (contentAlpha == 1f) {
+ resetAllContentAlphas();
+ }
}
+ /**
+ * If a subclass's {@link #getContentView()} returns different views depending on state,
+ * this method is an opportunity to reset the alpha of ALL content views, not just the
+ * current one, which may prevent a content view that is temporarily hidden from being reset.
+ *
+ * This should setAlpha(1.0f) and setLayerType(LAYER_TYPE_NONE) for all content views.
+ */
+ protected void resetAllContentAlphas() {}
+
@Override
protected void applyRoundness() {
super.applyRoundness();
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 a2d1040..819f5a3 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
@@ -87,6 +87,7 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -125,7 +126,8 @@
* the group summary (which contains 1 or more child notifications).
*/
public class ExpandableNotificationRow extends ActivatableNotificationView
- implements PluginListener<NotificationMenuRowPlugin>, SwipeableView {
+ implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
+ NotificationFadeAware.FadeOptimizedNotification {
private static final boolean DEBUG = false;
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
@@ -138,6 +140,7 @@
private boolean mUpdateBackgroundOnUpdate;
private boolean mNotificationTranslationFinished = false;
private boolean mIsSnoozed;
+ private boolean mIsFaded;
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -2127,15 +2130,6 @@
}
public void setExpandAnimationRunning(boolean expandAnimationRunning) {
- View contentView;
- if (mIsSummaryWithChildren) {
- contentView = mChildrenContainer;
- } else {
- contentView = getShowingLayout();
- }
- if (mGuts != null && mGuts.isExposed()) {
- contentView = mGuts;
- }
if (expandAnimationRunning) {
setAboveShelf(true);
mExpandAnimationRunning = true;
@@ -2148,9 +2142,7 @@
if (mGuts != null) {
mGuts.setAlpha(1.0f);
}
- if (contentView != null) {
- contentView.setAlpha(1.0f);
- }
+ resetAllContentAlphas();
setExtraWidthForClipping(0.0f);
if (mNotificationParent != null) {
mNotificationParent.setExtraWidthForClipping(0.0f);
@@ -2598,10 +2590,8 @@
mPrivateLayout.animate().cancel();
if (mChildrenContainer != null) {
mChildrenContainer.animate().cancel();
- mChildrenContainer.setAlpha(1f);
}
- mPublicLayout.setAlpha(1f);
- mPrivateLayout.setAlpha(1f);
+ resetAllContentAlphas();
mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
updateChildrenVisibility();
} else {
@@ -2628,7 +2618,10 @@
.alpha(0f)
.setStartDelay(delay)
.setDuration(duration)
- .withEndAction(() -> hiddenView.setVisibility(View.INVISIBLE));
+ .withEndAction(() -> {
+ hiddenView.setVisibility(View.INVISIBLE);
+ resetAllContentAlphas();
+ });
}
for (View showView : shownChildren) {
showView.setVisibility(View.VISIBLE);
@@ -2751,13 +2744,11 @@
if (wasAppearing) {
// During the animation the visible view might have changed, so let's make sure all
// alphas are reset
- if (mChildrenContainer != null) {
- mChildrenContainer.setAlpha(1.0f);
- mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
- }
- for (NotificationContentView l : mLayouts) {
- l.setAlpha(1.0f);
- l.setLayerType(LAYER_TYPE_NONE, null);
+ resetAllContentAlphas();
+ if (FADE_LAYER_OPTIMIZATION_ENABLED) {
+ setNotificationFaded(false);
+ } else {
+ setNotificationFadedOnChildren(false);
}
} else {
setHeadsUpAnimatingAway(false);
@@ -2765,6 +2756,110 @@
}
@Override
+ protected void resetAllContentAlphas() {
+ mPrivateLayout.setAlpha(1f);
+ mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null);
+ mPublicLayout.setAlpha(1f);
+ mPublicLayout.setLayerType(LAYER_TYPE_NONE, null);
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setAlpha(1f);
+ mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ }
+
+ /** Gets the last value set with {@link #setNotificationFaded(boolean)} */
+ @Override
+ public boolean isNotificationFaded() {
+ return mIsFaded;
+ }
+
+ /**
+ * This class needs to delegate the faded state set on it by
+ * {@link com.android.systemui.statusbar.notification.stack.ViewState} to its children.
+ * Having each notification use layerType of HARDWARE anytime it fades in/out can result in
+ * extremely large layers (in the case of groups, it can even exceed the device height).
+ * Because these large renders can cause serious jank when rendering, we instead have
+ * notifications return false from {@link #hasOverlappingRendering()} and delegate the
+ * layerType to child views which really need it in order to render correctly, such as icon
+ * views or the conversation face pile.
+ *
+ * Another compounding factor for notifications is that we change clipping on each frame of the
+ * animation, so the hardware layer isn't able to do any caching at the top level, but the
+ * individual elements we render with hardware layers (e.g. icons) cache wonderfully because we
+ * never invalidate them.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ mIsFaded = faded;
+ if (childrenRequireOverlappingRendering()) {
+ // == Simple Scenario ==
+ // If a child (like remote input) needs this to have overlapping rendering, then set
+ // the layerType of this view and reset the children to render as if the notification is
+ // not fading.
+ NotificationFadeAware.setLayerTypeForFaded(this, faded);
+ setNotificationFadedOnChildren(false);
+ } else {
+ // == Delegating Scenario ==
+ // This is the new normal for alpha: Explicitly reset this view's layer type to NONE,
+ // and require that all children use their own hardware layer if they have bad
+ // overlapping rendering.
+ NotificationFadeAware.setLayerTypeForFaded(this, false);
+ setNotificationFadedOnChildren(faded);
+ }
+ }
+
+ /** Private helper for iterating over the layouts and children containers to set faded state */
+ private void setNotificationFadedOnChildren(boolean faded) {
+ delegateNotificationFaded(mChildrenContainer, faded);
+ for (NotificationContentView layout : mLayouts) {
+ delegateNotificationFaded(layout, faded);
+ }
+ }
+
+ private static void delegateNotificationFaded(@Nullable View view, boolean faded) {
+ if (FADE_LAYER_OPTIMIZATION_ENABLED && view instanceof NotificationFadeAware) {
+ ((NotificationFadeAware) view).setNotificationFaded(faded);
+ } else {
+ NotificationFadeAware.setLayerTypeForFaded(view, faded);
+ }
+ }
+
+ /**
+ * Only declare overlapping rendering if independent children of the view require it.
+ */
+ @Override
+ public boolean hasOverlappingRendering() {
+ return super.hasOverlappingRendering() && childrenRequireOverlappingRendering();
+ }
+
+ /**
+ * Because RemoteInputView is designed to be an opaque view that overlaps the Actions row, the
+ * row should require overlapping rendering to ensure that the overlapped view doesn't bleed
+ * through when alpha fading.
+ *
+ * Note that this currently works for top-level notifications which squish their height down
+ * while collapsing the shade, but does not work for children inside groups, because the
+ * accordion affect does not apply to those views, so super.hasOverlappingRendering() will
+ * always return false to avoid the clipping caused when the view's measured height is less than
+ * the 'actual height'.
+ */
+ private boolean childrenRequireOverlappingRendering() {
+ if (!FADE_LAYER_OPTIMIZATION_ENABLED) {
+ return true;
+ }
+ // The colorized background is another layer with which all other elements overlap
+ if (getEntry().getSbn().getNotification().isColorized()) {
+ return true;
+ }
+ // Check if the showing layout has a need for overlapping rendering.
+ // NOTE: We could check both public and private layouts here, but becuause these states
+ // don't animate well, there are bigger visual artifacts if we start changing the shown
+ // layout during shade expansion.
+ NotificationContentView showingLayout = getShowingLayout();
+ return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
+ }
+
+ @Override
public int getExtraBottomPadding() {
if (mIsSummaryWithChildren && isGroupExpanded()) {
return mIncreasedPaddingBetweenElements;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index caba3ac..c661408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -28,6 +28,7 @@
import com.android.internal.widget.ConversationLayout;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
/**
* A hybrid view which may contain information about one ore more conversations.
@@ -138,4 +139,14 @@
lp.height = size;
view.setLayoutParams(lp);
}
+
+ /**
+ * Apply the faded state as a layer type change to the face pile view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mConversationFacePile, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index bc2adac3..c0d85a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -28,13 +28,14 @@
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.TransformState;
/**
* A hybrid view which may contain information about one ore more notifications.
*/
public class HybridNotificationView extends AlphaOptimizedLinearLayout
- implements TransformableView {
+ implements TransformableView, NotificationFadeAware {
protected final ViewTransformationHelper mTransformationHelper = new ViewTransformationHelper();
protected TextView mTitleView;
@@ -148,4 +149,7 @@
setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
mTransformationHelper.setVisible(visible);
}
+
+ @Override
+ public void setNotificationFaded(boolean faded) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0ffca30..9cc484c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -46,6 +46,7 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -71,7 +72,7 @@
* expanded and heads up layout. This class is responsible for clipping the content and and
* switching between the expanded, contracted and the heads up view depending on its clipped size.
*/
-public class NotificationContentView extends FrameLayout {
+public class NotificationContentView extends FrameLayout implements NotificationFadeAware {
private static final String TAG = "NotificationContentView";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -1994,4 +1995,39 @@
}
return Notification.COLOR_INVALID;
}
+
+ /**
+ * Delegate the faded state to the notification content views which actually
+ * need to have overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ if (mContractedWrapper != null) {
+ mContractedWrapper.setNotificationFaded(faded);
+ }
+ if (mHeadsUpWrapper != null) {
+ mHeadsUpWrapper.setNotificationFaded(faded);
+ }
+ if (mExpandedWrapper != null) {
+ mExpandedWrapper.setNotificationFaded(faded);
+ }
+ if (mSingleLineView != null) {
+ mSingleLineView.setNotificationFaded(faded);
+ }
+ }
+
+ /**
+ * @return true if a visible view has a remote input active, as this requires that the entire
+ * row report that it has overlapping rendering.
+ */
+ public boolean requireRowToHaveOverlappingRendering() {
+ // This inexpensive check is done on both states to avoid state invalidating the result.
+ if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
+ return true;
+ }
+ if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index bfe352d..7269f55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -146,6 +146,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
+ dispatchConfigurationChanged(getResources().getConfiguration());
}
@Override
@@ -254,7 +255,7 @@
return new NotificationSnoozeOption(null, minutes, description, resultText, action);
}
SpannableString string = new SpannableString(resultText);
- string.setSpan(new StyleSpan(Typeface.BOLD),
+ string.setSpan(new StyleSpan(Typeface.BOLD, res.getConfiguration().fontWeightAdjustment),
index, index + description.length(), 0 /* flags */);
return new NotificationSnoozeOption(null, minutes, description, string,
action);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
index 12e94cb..bb43b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -21,6 +21,7 @@
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.CallLayout
import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -37,6 +38,7 @@
NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
private val callLayout: CallLayout = view as CallLayout
+ private lateinit var conversationIconContainer: View
private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandBtn: View
@@ -45,6 +47,8 @@
private fun resolveViews() {
with(callLayout) {
+ conversationIconContainer =
+ requireViewById(com.android.internal.R.id.conversation_icon_container)
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
@@ -82,4 +86,14 @@
}
override fun getMinLayoutHeight(): Int = minHeightWithActions
+
+ /**
+ * Apply the faded state as a layer type change to the face pile view which needs to have
+ * overlapping contents render precisely.
+ */
+ override fun setNotificationFaded(faded: Boolean) {
+ // Do not call super
+ NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
+ NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 3ef5b665..e136055 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -23,6 +23,7 @@
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingLinearLayout
import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform
@@ -42,6 +43,7 @@
)
private val conversationLayout: ConversationLayout = view as ConversationLayout
+ private lateinit var conversationIconContainer: View
private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandBtn: View
@@ -59,6 +61,8 @@
messagingLinearLayout = conversationLayout.messagingLinearLayout
imageMessageContainer = conversationLayout.imageMessageContainer
with(conversationLayout) {
+ conversationIconContainer =
+ requireViewById(com.android.internal.R.id.conversation_icon_container)
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
@@ -136,4 +140,10 @@
minHeightWithActions
else
super.getMinLayoutHeight()
+
+ override fun setNotificationFaded(faded: Boolean) {
+ // Do not call super
+ NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
+ NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index 4c9c2f9..fdff12d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -22,6 +22,7 @@
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -86,4 +87,14 @@
public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
return true;
}
+
+ /**
+ * Apply the faded state as a layer type change to the custom view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mView, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 8c6fa02..3159539 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -20,6 +20,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -67,4 +68,14 @@
}
super.onContentUpdated(row);
}
+
+ /**
+ * Apply the faded state as a layer type change to the custom view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mWrappedView, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 7630191..6c3e0d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -42,6 +42,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -395,4 +396,13 @@
*/
public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
}
+
+ /**
+ * Apply the faded state as a layer type change to the views which need to have overlapping
+ * contents render precisely.
+ */
+ public void setNotificationFaded(boolean faded) {
+ NotificationFadeAware.setLayerTypeForFaded(getIcon(), faded);
+ NotificationFadeAware.setLayerTypeForFaded(getExpandButton(), faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index a9cc3237..e658468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -59,6 +59,7 @@
private float mMaxHeadsUpTranslation;
private boolean mDismissAllInProgress;
private int mLayoutMinHeight;
+ private int mLayoutMaxHeight;
private NotificationShelf mShelf;
private int mZDistanceBetweenElements;
private int mBaseZHeight;
@@ -326,6 +327,14 @@
mLayoutHeight = layoutHeight;
}
+ public void setLayoutMaxHeight(int maxLayoutHeight) {
+ mLayoutMaxHeight = maxLayoutHeight;
+ }
+
+ public int getLayoutMaxHeight() {
+ return mLayoutMaxHeight;
+ }
+
public float getTopPadding() {
return mTopPadding;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index c2716b9..7ece3d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -38,6 +38,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -51,7 +52,8 @@
/**
* A container containing child notifications
*/
-public class NotificationChildrenContainer extends ViewGroup {
+public class NotificationChildrenContainer extends ViewGroup
+ implements NotificationFadeAware {
@VisibleForTesting
static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
@@ -111,6 +113,7 @@
private int mCurrentHeaderTranslation = 0;
private float mHeaderVisibleAmount = 1.0f;
private int mUntruncatedChildCount;
+ private boolean mContainingNotificationIsFaded = false;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -277,6 +280,7 @@
mDividers.add(newIndex, divider);
row.setContentTransformationAmount(0, false /* isLastChild */);
+ row.setNotificationFaded(mContainingNotificationIsFaded);
// It doesn't make sense to keep old animations around, lets cancel them!
ExpandableViewState viewState = row.getViewState();
if (viewState != null) {
@@ -301,6 +305,7 @@
});
row.setSystemChildExpanded(false);
+ row.setNotificationFaded(false);
row.setUserLocked(false);
if (!row.isRemoved()) {
mGroupingUtil.restoreChildNotification(row);
@@ -1308,4 +1313,18 @@
mNotificationHeaderWrapperLowPriority.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
}
}
+
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ mContainingNotificationIsFaded = faded;
+ if (mNotificationHeaderWrapper != null) {
+ mNotificationHeaderWrapper.setNotificationFaded(faded);
+ }
+ if (mNotificationHeaderWrapperLowPriority != null) {
+ mNotificationHeaderWrapperLowPriority.setNotificationFaded(faded);
+ }
+ for (ExpandableNotificationRow child : mAttachedChildren) {
+ child.setNotificationFaded(faded);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6a12710..73a48c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -119,6 +119,7 @@
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -410,7 +411,7 @@
private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1;
private float mKeyguardBottomPadding = -1;
- private int mStatusBarHeight;
+ @VisibleForTesting int mStatusBarHeight;
private int mMinInteractionHeight;
private final Rect mClipRect = new Rect();
private boolean mIsClipped;
@@ -667,6 +668,7 @@
public void setIsRemoteInputActive(boolean isActive) {
mIsRemoteInputActive = isActive;
+ updateFooter();
}
@VisibleForTesting
@@ -681,6 +683,7 @@
boolean showFooterView = (showDismissView || getVisibleNotificationCount() > 0)
&& mIsCurrentUserSetup // see: b/193149550
&& mStatusBarState != StatusBarState.KEYGUARD
+ && mQsExpansionFraction != 1
&& !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
&& !mIsRemoteInputActive;
boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
@@ -726,38 +729,59 @@
}
if (DEBUG) {
- int y = mTopPadding;
- mDebugPaint.setColor(Color.RED);
- canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
- y = getLayoutHeight();
- mDebugPaint.setColor(Color.YELLOW);
- canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
- y = (int) mMaxLayoutHeight;
- mDebugPaint.setColor(Color.MAGENTA);
- canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
- if (mKeyguardBottomPadding >= 0) {
- y = getHeight() - (int) mKeyguardBottomPadding;
- mDebugPaint.setColor(Color.GRAY);
- canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
- }
-
- y = getHeight() - getEmptyBottomMargin();
- mDebugPaint.setColor(Color.GREEN);
- canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
- y = (int) (mAmbientState.getStackY());
- mDebugPaint.setColor(Color.CYAN);
- canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
- y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
- mDebugPaint.setColor(Color.BLUE);
- canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+ onDrawDebug(canvas);
}
}
+ /** Used to track the Y positions that were already used to draw debug text labels. */
+ private static final Set<Integer> DEBUG_TEXT_USED_Y_POSITIONS =
+ DEBUG ? new HashSet<>() : Collections.emptySet();
+
+ private void onDrawDebug(Canvas canvas) {
+ DEBUG_TEXT_USED_Y_POSITIONS.clear();
+
+ int y = mTopPadding;
+ drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding");
+
+ y = getLayoutHeight();
+ drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight()");
+
+ y = (int) mMaxLayoutHeight;
+ drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight");
+
+ if (mKeyguardBottomPadding >= 0) {
+ y = getHeight() - (int) mKeyguardBottomPadding;
+ drawDebugInfo(canvas, y, Color.GRAY,
+ /* label= */ "getHeight() - mKeyguardBottomPadding");
+ }
+
+ y = getHeight() - getEmptyBottomMargin();
+ drawDebugInfo(canvas, y, Color.GREEN, /* label= */ "getHeight() - getEmptyBottomMargin()");
+
+ y = (int) (mAmbientState.getStackY());
+ drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY()");
+
+ y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
+ drawDebugInfo(canvas, y, Color.BLUE,
+ /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight()");
+ }
+
+ private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
+ mDebugPaint.setColor(color);
+ canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ getWidth(), /* stopY= */ y,
+ mDebugPaint);
+ canvas.drawText(label, /* x= */ 0, /* y= */ computeDebugYTextPosition(y), mDebugPaint);
+ }
+
+ private int computeDebugYTextPosition(int lineY) {
+ int textY = lineY;
+ while (DEBUG_TEXT_USED_Y_POSITIONS.contains(textY)) {
+ textY = (int) (textY + mDebugPaint.getTextSize());
+ }
+ DEBUG_TEXT_USED_Y_POSITIONS.add(textY);
+ return textY;
+ }
+
@ShadeViewRefactor(RefactorComponent.DECORATOR)
private void drawBackground(Canvas canvas) {
int lockScreenLeft = mSidePaddings;
@@ -1087,6 +1111,7 @@
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void updateAlgorithmHeightAndPadding() {
mAmbientState.setLayoutHeight(getLayoutHeight());
+ mAmbientState.setLayoutMaxHeight(mMaxLayoutHeight);
updateAlgorithmLayoutMinHeight();
mAmbientState.setTopPadding(mTopPadding);
}
@@ -3962,6 +3987,7 @@
updateChronometers();
requestChildrenUpdate();
updateUseRoundedRectClipping();
+ updateDismissBehavior();
}
}
@@ -4744,6 +4770,8 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setQsExpansionFraction(float qsExpansionFraction) {
+ boolean footerAffected = mQsExpansionFraction != qsExpansionFraction
+ && (mQsExpansionFraction == 1 || qsExpansionFraction == 1);
mQsExpansionFraction = qsExpansionFraction;
updateUseRoundedRectClipping();
@@ -4752,6 +4780,9 @@
if (mOwnScrollY > 0) {
setOwnScrollY((int) MathUtils.lerp(mOwnScrollY, 0, mQsExpansionFraction));
}
+ if (footerAffected) {
+ updateFooter();
+ }
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
@@ -4820,8 +4851,12 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getMinExpansionHeight() {
+ // shelf height is defined in dp but status bar height can be defined in px, that makes
+ // relation between them variable - sometimes one might be bigger than the other when
+ // changing density. That’s why we need to ensure we’re not subtracting negative value below
return mShelf.getIntrinsicHeight()
- - (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2
+ - Math.max(0,
+ (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2)
+ mWaterfallTopInset;
}
@@ -4903,6 +4938,10 @@
StringBuilder sb = new StringBuilder("[")
.append(this.getClass().getSimpleName()).append(":")
.append(" pulsing=").append(mPulsing ? "T" : "f")
+ .append(" expanded=").append(mIsExpanded ? "T" : "f")
+ .append(" headsUpPinned=").append(mInHeadsUpPinnedMode ? "T" : "f")
+ .append(" qsClipping=").append(mShouldUseRoundedRectClipping ? "T" : "f")
+ .append(" qsClipDismiss=").append(mDismissUsingRowTranslationX ? "T" : "f")
.append(" visibility=").append(DumpUtilsKt.visibilityString(getVisibility()))
.append(" alpha=").append(getAlpha())
.append(" scrollY=").append(mAmbientState.getScrollY())
@@ -5430,7 +5469,7 @@
// On the split keyguard, dismissing with clipping without a visual boundary looks odd,
// so let's use the content dismiss behavior instead.
boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade
- || mStatusBarState != StatusBarState.KEYGUARD;
+ || (mStatusBarState != StatusBarState.KEYGUARD && mIsExpanded);
if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) {
mDismissUsingRowTranslationX = dismissUsingRowTranslationX;
for (int i = 0; i < getChildCount(); i++) {
@@ -6092,6 +6131,14 @@
return mExpandHelperCallback;
}
+ float getAppearFraction() {
+ return mLastSentAppear;
+ }
+
+ float getExpandedHeight() {
+ return mLastSentExpandedHeight;
+ }
+
/** Enum for selecting some or all notification rows (does not included non-notif views). */
@Retention(SOURCE)
@IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
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 03fc767..f14cc93c 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
@@ -858,6 +858,14 @@
mView.setHeadsUpAppearanceController(controller);
}
+ public float getAppearFraction() {
+ return mView.getAppearFraction();
+ }
+
+ public float getExpandedHeight() {
+ return mView.getExpandedHeight();
+ }
+
public void requestLayout() {
mView.requestLayout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 6647769..a12fd4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -444,8 +444,8 @@
final int height = (view instanceof ExpandableView)
? ((ExpandableView) view).getActualHeight()
: view.getHeight();
- final int rx = (int) ev.getX();
- final int ry = (int) ev.getY();
+ final int rx = (int) ev.getRawX();
+ final int ry = (int) ev.getRawY();
int[] temp = new int[2];
view.getLocationOnScreen(temp);
final int x = temp[0];
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 015edb8..2c70a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -29,6 +29,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -60,6 +61,7 @@
@VisibleForTesting float mHeadsUpInset;
private int mPinnedZTranslationExtra;
private float mNotificationScrimPadding;
+ private int mCloseHandleUnderlapHeight;
public StackScrollAlgorithm(
Context context,
@@ -85,6 +87,7 @@
R.dimen.heads_up_pinned_elevation);
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+ mCloseHandleUnderlapHeight = res.getDimensionPixelSize(R.dimen.close_handle_underlap);
}
/**
@@ -459,13 +462,17 @@
&& !hasOngoingNotifs(algorithmState));
}
} else {
- if (view != ambientState.getTrackedHeadsUpRow()) {
+ if (view instanceof EmptyShadeView) {
+ float fullHeight = ambientState.getLayoutMaxHeight() + mCloseHandleUnderlapHeight
+ - ambientState.getStackY();
+ viewState.yTranslation = (fullHeight - getMaxAllowedChildHeight(view)) / 2f;
+ } else if (view != ambientState.getTrackedHeadsUpRow()) {
if (ambientState.isExpansionChanging()) {
// We later update shelf state, then hide views below the shelf.
viewState.hidden = false;
viewState.inShelf = algorithmState.firstViewInShelf != null
&& i >= algorithmState.visibleChildren.indexOf(
- algorithmState.firstViewInShelf);
+ algorithmState.firstViewInShelf);
} else if (ambientState.getShelf() != null) {
// When pulsing (incoming notification on AOD), innerHeight is 0; clamp all
// to shelf start, thereby hiding all notifications (except the first one, which
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 6d82a45..83bea84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -29,6 +29,7 @@
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -206,14 +207,26 @@
} else if (view.getAlpha() != this.alpha) {
// apply layer type
boolean becomesFullyVisible = this.alpha == 1.0f;
- boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
- && view.hasOverlappingRendering();
- int layerType = view.getLayerType();
- int newLayerType = newLayerTypeIsHardware
- ? View.LAYER_TYPE_HARDWARE
- : View.LAYER_TYPE_NONE;
- if (layerType != newLayerType) {
- view.setLayerType(newLayerType, null);
+ boolean becomesFaded = !becomesInvisible && !becomesFullyVisible;
+ if (FadeOptimizedNotification.FADE_LAYER_OPTIMIZATION_ENABLED
+ && view instanceof FadeOptimizedNotification) {
+ // NOTE: A view that's going to utilize this interface to avoid having a hardware
+ // layer will have to return false from hasOverlappingRendering(), so we
+ // intentionally do not check that value in this if, even though we do in the else.
+ FadeOptimizedNotification fadeOptimizedView = (FadeOptimizedNotification) view;
+ boolean isFaded = fadeOptimizedView.isNotificationFaded();
+ if (isFaded != becomesFaded) {
+ fadeOptimizedView.setNotificationFaded(becomesFaded);
+ }
+ } else {
+ boolean newLayerTypeIsHardware = becomesFaded && view.hasOverlappingRendering();
+ int layerType = view.getLayerType();
+ int newLayerType = newLayerTypeIsHardware
+ ? View.LAYER_TYPE_HARDWARE
+ : View.LAYER_TYPE_NONE;
+ if (layerType != newLayerType) {
+ view.setLayerType(newLayerType, null);
+ }
}
// apply alpha
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 67f51cb..98b5dcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.PowerManager;
@@ -46,9 +47,11 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
@@ -71,6 +74,7 @@
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
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 FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 2;
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
@@ -167,6 +171,10 @@
private final MetricsLogger mMetricsLogger;
private final AuthController mAuthController;
+ private final StatusBarStateController mStatusBarStateController;
+
+ private long mLastFpFailureUptimeMillis;
+ private int mNumConsecutiveFpFailures;
private static final class PendingAuthenticated {
public final int userId;
@@ -209,7 +217,10 @@
BIOMETRIC_IRIS_FAILURE(403),
@UiEvent(doc = "A biometric event of type iris errored.")
- BIOMETRIC_IRIS_ERROR(404);
+ BIOMETRIC_IRIS_ERROR(404),
+
+ @UiEvent(doc = "Bouncer was shown as a result of consecutive failed UDFPS attempts.")
+ BIOMETRIC_BOUNCER_SHOWN(916);
private final int mId;
@@ -257,7 +268,8 @@
NotificationMediaManager notificationMediaManager,
WakefulnessLifecycle wakefulnessLifecycle,
ScreenLifecycle screenLifecycle,
- AuthController authController) {
+ AuthController authController,
+ StatusBarStateController statusBarStateController) {
mContext = context;
mPowerManager = powerManager;
mShadeController = shadeController;
@@ -279,6 +291,7 @@
mKeyguardBypassController.setUnlockController(this);
mMetricsLogger = metricsLogger;
mAuthController = authController;
+ mStatusBarStateController = statusBarStateController;
dumpManager.registerDumpable(getClass().getName(), this);
}
@@ -620,6 +633,22 @@
.setType(MetricsEvent.TYPE_FAILURE).setSubtype(toSubtype(biometricSourceType)));
Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
+
+ long currUptimeMillis = SystemClock.uptimeMillis();
+ if (currUptimeMillis - mLastFpFailureUptimeMillis < 2000) { // attempt within 2 seconds
+ mNumConsecutiveFpFailures += 1;
+ } else {
+ mNumConsecutiveFpFailures = 1;
+ }
+ mLastFpFailureUptimeMillis = currUptimeMillis;
+
+ if (biometricSourceType.equals(BiometricSourceType.FINGERPRINT)
+ && mUpdateMonitor.isUdfpsSupported()
+ && mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
+ startWakeAndUnlock(MODE_SHOW_BOUNCER);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+ mNumConsecutiveFpFailures = 0;
+ }
cleanup();
}
@@ -631,6 +660,17 @@
.addTaggedData(MetricsEvent.FIELD_BIOMETRIC_AUTH_ERROR, msgId));
Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
+
+ // if we're on the shade and we're locked out, immediately show the bouncer
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT
+ && (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ && mUpdateMonitor.isUdfpsSupported()
+ && (mStatusBarStateController.getState() == StatusBarState.SHADE
+ || mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) {
+ startWakeAndUnlock(MODE_SHOW_BOUNCER);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+ }
cleanup();
}
@@ -664,6 +704,8 @@
mBiometricModeListener.onResetMode();
mBiometricModeListener.notifyBiometricAuthModeChanged();
}
+ mNumConsecutiveFpFailures = 0;
+ mLastFpFailureUptimeMillis = 0;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 49e3fe7..b5beca4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
@@ -26,7 +27,10 @@
import android.util.MathUtils;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -35,7 +39,9 @@
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.tuner.TunerService;
@@ -53,7 +59,8 @@
public class DozeParameters implements
TunerService.Tunable,
com.android.systemui.plugins.statusbar.DozeParameters,
- Dumpable {
+ Dumpable, ConfigurationController.ConfigurationListener,
+ StatusBarStateController.StateListener {
private static final int MAX_DURATION = 60 * 1000;
public static final boolean FORCE_NO_BLANKING =
SystemProperties.getBoolean("debug.force_no_blanking", false);
@@ -74,6 +81,22 @@
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
+ private boolean mKeyguardShowing;
+ @VisibleForTesting
+ final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ mKeyguardShowing = showing;
+ updateControlScreenOff();
+ }
+
+ @Override
+ public void onShadeExpandedChanged(boolean expanded) {
+ updateControlScreenOff();
+ }
+ };
+
@Inject
protected DozeParameters(
@Main Resources resources,
@@ -84,7 +107,10 @@
TunerService tunerService,
DumpManager dumpManager,
FeatureFlags featureFlags,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ ConfigurationController configurationController,
+ StatusBarStateController statusBarStateController) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -97,10 +123,13 @@
mFeatureFlags = featureFlags;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+ keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
tunerService.addTunable(
this,
Settings.Secure.DOZE_ALWAYS_ON,
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ configurationController.addCallback(this);
+ statusBarStateController.addCallback(this);
}
public boolean getDisplayStateSupported() {
@@ -222,12 +251,28 @@
}
/**
+ *
+ */
+ public void updateControlScreenOff() {
+ if (!getDisplayNeedsBlanking()) {
+ final boolean controlScreenOff =
+ getAlwaysOn() && (mKeyguardShowing || shouldControlUnlockedScreenOff());
+ setControlScreenOffAnimation(controlScreenOff);
+ }
+ }
+
+ /**
* Whether we want to control the screen off animation when the device is unlocked. If we do,
* we'll animate in AOD before turning off the screen, rather than simply fading to black and
* then abruptly showing AOD.
+ *
+ * There are currently several reasons we might not want to control the screen off even if we
+ * are able to, such as the shade being expanded, being in landscape, or having animations
+ * disabled for a11y.
*/
public boolean shouldControlUnlockedScreenOff() {
- return mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation();
+ return canControlUnlockedScreenOff()
+ && mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation();
}
/**
@@ -308,12 +353,27 @@
@Override
public void onTuningChanged(String key, String newValue) {
mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
+
+ if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) {
+ updateControlScreenOff();
+ }
+
for (Callback callback : mCallbacks) {
callback.onAlwaysOnChange();
}
}
@Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateControlScreenOff();
+ }
+
+ @Override
+ public void onStatePostChange() {
+ updateControlScreenOff();
+ }
+
+ @Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.print("getAlwaysOn(): "); pw.println(getAlwaysOn());
pw.print("getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
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 57b9c03..a88a3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -63,7 +63,6 @@
private final DozeLog mDozeLog;
private final PowerManager mPowerManager;
private boolean mAnimateWakeup;
- private boolean mAnimateScreenOff;
private boolean mIgnoreTouchWhilePulsing;
private Runnable mPendingScreenOffCallback;
@VisibleForTesting
@@ -357,11 +356,6 @@
}
@Override
- public void setAnimateScreenOff(boolean animateScreenOff) {
- mAnimateScreenOff = animateScreenOff;
- }
-
- @Override
public void onSlpiTap(float screenX, float screenY) {
if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
&& mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
@@ -440,10 +434,6 @@
return mAnimateWakeup;
}
- boolean shouldAnimateScreenOff() {
- return mAnimateScreenOff;
- }
-
boolean getIgnoreTouchWhilePulsing() {
return mIgnoreTouchWhilePulsing;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 927b4c8..8a7cf36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -24,6 +24,7 @@
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -35,23 +36,29 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.ViewController;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* Controls the appearance of heads up notifications in the icon area and the header itself.
*/
-public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
- DarkIconDispatcher.DarkReceiver, NotificationWakeUpCoordinator.WakeUpListener {
+@StatusBarFragmentScope
+public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView>
+ implements OnHeadsUpChangedListener,
+ DarkIconDispatcher.DarkReceiver,
+ NotificationWakeUpCoordinator.WakeUpListener {
public static final int CONTENT_FADE_DURATION = 110;
public static final int CONTENT_FADE_DELAY = 100;
private final NotificationIconAreaController mNotificationIconAreaController;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationStackScrollLayoutController mStackScrollerController;
- private final HeadsUpStatusBarView mHeadsUpStatusBarView;
private final View mCenteredIconView;
private final View mClockView;
private final View mOperatorNameView;
@@ -67,8 +74,6 @@
@VisibleForTesting
float mExpandedHeight;
@VisibleForTesting
- boolean mIsExpanded;
- @VisibleForTesting
float mAppearFraction;
private ExpandableNotificationRow mTrackedChild;
private boolean mShown;
@@ -83,7 +88,7 @@
Point mPoint;
private KeyguardStateController mKeyguardStateController;
-
+ @Inject
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
HeadsUpManagerPhone headsUpManager,
@@ -92,11 +97,15 @@
KeyguardBypassController keyguardBypassController,
KeyguardStateController keyguardStateController,
NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue,
- NotificationPanelViewController notificationPanelViewController, View statusBarView) {
+ NotificationPanelViewController notificationPanelViewController,
+ @RootView PhoneStatusBarView statusBarView) {
this(notificationIconAreaController, headsUpManager, statusBarStateController,
keyguardBypassController, wakeUpCoordinator, keyguardStateController,
commandQueue, notificationStackScrollLayoutController,
notificationPanelViewController,
+ // TODO(b/205609837): We should have the StatusBarFragmentComponent provide these
+ // four views, and then we can delete this constructor and just use the one below
+ // (which also removes the undesirable @VisibleForTesting).
statusBarView.findViewById(R.id.heads_up_status_bar_view),
statusBarView.findViewById(R.id.clock),
statusBarView.findViewById(R.id.operator_name_frame),
@@ -118,25 +127,27 @@
View clockView,
View operatorNameView,
View centeredIconView) {
+ super(headsUpStatusBarView);
mNotificationIconAreaController = notificationIconAreaController;
mHeadsUpManager = headsUpManager;
- mHeadsUpManager.addListener(this);
- mHeadsUpStatusBarView = headsUpStatusBarView;
mCenteredIconView = centeredIconView;
- headsUpStatusBarView.setOnDrawingRectChangedListener(
- () -> updateIsolatedIconLocation(true /* requireUpdate */));
+
+ // We may be mid-HUN-expansion when this controller is re-created (for example, if the user
+ // has started pulling down the notification shade from the HUN and then the font size
+ // changes). We need to re-fetch these values since they're used to correctly display the
+ // HUN during this shade expansion.
+ mTrackedChild = notificationPanelViewController.getTrackedHeadsUpNotification();
+ mAppearFraction = stackScrollerController.getAppearFraction();
+ mExpandedHeight = stackScrollerController.getExpandedHeight();
+
mStackScrollerController = stackScrollerController;
mNotificationPanelViewController = notificationPanelViewController;
- notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
- notificationPanelViewController.setHeadsUpAppearanceController(this);
- mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScrollerController.setHeadsUpAppearanceController(this);
mClockView = clockView;
mOperatorNameView = operatorNameView;
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
- mDarkIconDispatcher.addDarkReceiver(this);
- mHeadsUpStatusBarView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
@@ -146,21 +157,32 @@
// trigger scroller to notify the latest panel translation
mStackScrollerController.requestLayout();
}
- mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
+ mView.removeOnLayoutChangeListener(this);
}
});
mBypassController = bypassController;
mStatusBarStateController = stateController;
mWakeUpCoordinator = wakeUpCoordinator;
- wakeUpCoordinator.addListener(this);
mCommandQueue = commandQueue;
mKeyguardStateController = keyguardStateController;
}
+ @Override
+ protected void onViewAttached() {
+ mHeadsUpManager.addListener(this);
+ mView.setOnDrawingRectChangedListener(
+ () -> updateIsolatedIconLocation(true /* requireUpdate */));
+ mWakeUpCoordinator.addListener(this);
+ mNotificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ mNotificationPanelViewController.setHeadsUpAppearanceController(this);
+ mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
+ mDarkIconDispatcher.addDarkReceiver(this);
+ }
- public void destroy() {
+ @Override
+ protected void onViewDetached() {
mHeadsUpManager.removeListener(this);
- mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
+ mView.setOnDrawingRectChangedListener(null);
mWakeUpCoordinator.removeListener(this);
mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
mNotificationPanelViewController.setHeadsUpAppearanceController(null);
@@ -170,7 +192,7 @@
private void updateIsolatedIconLocation(boolean requireStateUpdate) {
mNotificationIconAreaController.setIsolatedIconLocation(
- mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
+ mView.getIconDrawingRect(), requireStateUpdate);
}
@Override
@@ -184,20 +206,20 @@
if (shouldBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
}
- NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
- mHeadsUpStatusBarView.setEntry(newEntry);
+ NotificationEntry previousEntry = mView.getShowingEntry();
+ mView.setEntry(newEntry);
if (newEntry != previousEntry) {
boolean animateIsolation = false;
if (newEntry == null) {
// no heads up anymore, lets start the disappear animation
setShown(false);
- animateIsolation = !mIsExpanded;
+ animateIsolation = !isExpanded();
} else if (previousEntry == null) {
// We now have a headsUp and didn't have one before. Let's start the disappear
// animation
setShown(true);
- animateIsolation = !mIsExpanded;
+ animateIsolation = !isExpanded();
}
updateIsolatedIconLocation(false /* requireUpdate */);
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
@@ -210,8 +232,8 @@
mShown = isShown;
if (isShown) {
updateParentClipping(false /* shouldClip */);
- mHeadsUpStatusBarView.setVisibility(View.VISIBLE);
- show(mHeadsUpStatusBarView);
+ mView.setVisibility(View.VISIBLE);
+ show(mView);
hide(mClockView, View.INVISIBLE);
if (mCenteredIconView.getVisibility() != View.GONE) {
hide(mCenteredIconView, View.INVISIBLE);
@@ -227,21 +249,21 @@
if (mOperatorNameView != null) {
show(mOperatorNameView);
}
- hide(mHeadsUpStatusBarView, View.GONE, () -> {
+ hide(mView, View.GONE, () -> {
updateParentClipping(true /* shouldClip */);
});
}
// Show the status bar icons when the view gets shown / hidden
if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
mCommandQueue.recomputeDisableFlags(
- mHeadsUpStatusBarView.getContext().getDisplayId(), false);
+ mView.getContext().getDisplayId(), false);
}
}
}
private void updateParentClipping(boolean shouldClip) {
ViewClippingUtil.setClippingDeactivated(
- mHeadsUpStatusBarView, !shouldClip, mParentClippingParams);
+ mView, !shouldClip, mParentClippingParams);
}
/**
@@ -310,7 +332,7 @@
*/
public boolean shouldBeVisible() {
boolean notificationsShown = !mWakeUpCoordinator.getNotificationsFullyHidden();
- boolean canShow = !mIsExpanded && notificationsShown;
+ boolean canShow = !isExpanded() && notificationsShown;
if (mBypassController.getBypassEnabled() &&
(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
|| mKeyguardStateController.isKeyguardGoingAway())
@@ -328,17 +350,17 @@
public void setAppearFraction(float expandedHeight, float appearFraction) {
boolean changed = expandedHeight != mExpandedHeight;
+ boolean oldIsExpanded = isExpanded();
+
mExpandedHeight = expandedHeight;
mAppearFraction = appearFraction;
- boolean isExpanded = expandedHeight > 0;
// We only notify if the expandedHeight changed and not on the appearFraction, since
// otherwise we may run into an infinite loop where the panel and this are constantly
// updating themselves over just a small fraction
if (changed) {
updateHeadsUpHeaders();
}
- if (isExpanded != mIsExpanded) {
- mIsExpanded = isExpanded;
+ if (isExpanded() != oldIsExpanded) {
updateTopEntry();
}
}
@@ -358,6 +380,10 @@
}
}
+ private boolean isExpanded() {
+ return mExpandedHeight > 0;
+ }
+
private void updateHeadsUpHeaders() {
mHeadsUpManager.getAllEntries().forEach(entry -> {
updateHeader(entry);
@@ -376,22 +402,13 @@
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
- mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
+ mView.onDarkChanged(area, darkIntensity, tint);
}
public void onStateChanged() {
updateTopEntry();
}
- void readFrom(HeadsUpAppearanceController oldController) {
- if (oldController != null) {
- mTrackedChild = oldController.mTrackedChild;
- mExpandedHeight = oldController.mExpandedHeight;
- mIsExpanded = oldController.mIsExpanded;
- mAppearFraction = oldController.mAppearFraction;
- }
- }
-
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
updateTopEntry();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index e26f75f..aeef8cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -71,6 +71,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
@@ -150,6 +151,7 @@
private ControlsComponent mControlsComponent;
private boolean mControlServicesAvailable = false;
+ @Nullable private View mAmbientIndicationArea;
private ViewGroup mIndicationArea;
private TextView mIndicationText;
private TextView mIndicationTextBottom;
@@ -270,6 +272,29 @@
public void initFrom(KeyguardBottomAreaView oldBottomArea) {
setStatusBar(oldBottomArea.mStatusBar);
+
+ // if it exists, continue to use the original ambient indication container
+ // instead of the newly inflated one
+ if (mAmbientIndicationArea != null) {
+ // remove old ambient indication from its parent
+ View originalAmbientIndicationView =
+ oldBottomArea.findViewById(R.id.ambient_indication_container);
+ ((ViewGroup) originalAmbientIndicationView.getParent())
+ .removeView(originalAmbientIndicationView);
+
+ // remove current ambient indication from its parent (discard)
+ ViewGroup ambientIndicationParent = (ViewGroup) mAmbientIndicationArea.getParent();
+ int ambientIndicationIndex =
+ ambientIndicationParent.indexOfChild(mAmbientIndicationArea);
+ ambientIndicationParent.removeView(mAmbientIndicationArea);
+
+ // add the old ambient indication to this view
+ ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex);
+ mAmbientIndicationArea = originalAmbientIndicationView;
+
+ // update burn-in offsets
+ dozeTimeTick();
+ }
}
@Override
@@ -283,6 +308,7 @@
mWalletButton = findViewById(R.id.wallet_button);
mControlsButton = findViewById(R.id.controls_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
+ mAmbientIndicationArea = findViewById(R.id.ambient_indication_container);
mIndicationText = findViewById(R.id.keyguard_indication_text);
mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
mIndicationBottomMargin = getResources().getDimensionPixelSize(
@@ -901,6 +927,9 @@
int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */)
- mBurnInYOffset;
mIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
+ if (mAmbientIndicationArea != null) {
+ mAmbientIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
+ }
}
public void setAntiBurnInOffsetX(int burnInXOffset) {
@@ -909,6 +938,9 @@
}
mBurnInXOffset = burnInXOffset;
mIndicationArea.setTranslationX(burnInXOffset);
+ if (mAmbientIndicationArea != null) {
+ mAmbientIndicationArea.setTranslationX(burnInXOffset);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 353868b..9647486 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
@@ -81,6 +82,13 @@
public void onStrongAuthStateChanged(int userId) {
mBouncerPromptReason = mCallback.getBouncerPromptReason();
}
+
+ @Override
+ public void onLockedOutStateChanged(BiometricSourceType type) {
+ if (type == BiometricSourceType.FINGERPRINT) {
+ mBouncerPromptReason = mCallback.getBouncerPromptReason();
+ }
+ }
};
private final Runnable mRemoveViewRunnable = this::removeView;
private final KeyguardBypassController mKeyguardBypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 7ca8652..732e5f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -57,8 +57,7 @@
private int mUserSwitchPreferredY;
/**
- * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
- * avatar.
+ * Minimum top margin to avoid overlap with status bar or multi-user switcher avatar.
*/
private int mMinTopMargin;
@@ -203,7 +202,7 @@
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding;
} else if (mIsSplitShade) {
- return getClockY(1.0f, mDarkAmount);
+ return getClockY(1.0f, mDarkAmount) + mUserSwitchHeight;
} else {
return getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight;
}
@@ -213,7 +212,7 @@
if (mBypassEnabled) {
return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount);
} else if (mIsSplitShade) {
- return Math.max(0, clockYPosition - mSplitShadeTopNotificationsMargin);
+ return clockYPosition - mSplitShadeTopNotificationsMargin + mUserSwitchHeight;
} else {
return clockYPosition + mKeyguardStatusHeight;
}
@@ -223,7 +222,7 @@
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding;
} else if (mIsSplitShade) {
- return Math.max(mSplitShadeTargetTopMargin, mMinTopMargin);
+ return mSplitShadeTargetTopMargin + mUserSwitchHeight;
} else {
return mMinTopMargin + mKeyguardStatusHeight;
}
@@ -231,7 +230,7 @@
private int getExpandedPreferredClockY() {
if (mIsSplitShade) {
- return Math.max(mSplitShadeTargetTopMargin, mMinTopMargin);
+ return mSplitShadeTargetTopMargin;
} else {
return mMinTopMargin;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 3a68b9c..339f371 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -35,23 +35,20 @@
import com.android.systemui.animation.Interpolators;
import com.android.systemui.keyguard.KeyguardIndication;
-import java.util.LinkedList;
-
/**
* A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
*/
public class KeyguardIndicationTextView extends TextView {
- private static final long MSG_MIN_DURATION_MILLIS_DEFAULT = 1500;
-
@StyleRes
private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
@StyleRes
private static int sButtonStyleId = R.style.TextAppearance_Keyguard_BottomArea_Button;
- private long mNextAnimationTime = 0;
private boolean mAnimationsEnabled = true;
- private LinkedList<CharSequence> mMessages = new LinkedList<>();
- private LinkedList<KeyguardIndication> mKeyguardIndicationInfo = new LinkedList<>();
+ private CharSequence mMessage;
+ private KeyguardIndication mKeyguardIndicationInfo;
+
+ private Animator mLastAnimator;
public KeyguardIndicationTextView(Context context) {
super(context);
@@ -71,22 +68,24 @@
}
/**
- * Clears message queue.
+ * Clears message queue and currently shown message.
*/
public void clearMessages() {
- mMessages.clear();
- mKeyguardIndicationInfo.clear();
+ if (mLastAnimator != null) {
+ mLastAnimator.cancel();
+ }
+ setText("");
}
/**
- * Changes the text with an animation and makes sure a single indication is shown long enough.
+ * Changes the text with an animation.
*/
public void switchIndication(int textResId) {
switchIndication(getResources().getText(textResId), null);
}
/**
- * Changes the text with an animation and makes sure a single indication is shown long enough.
+ * Changes the text with an animation.
*
* @param indication The text to show.
*/
@@ -95,15 +94,14 @@
}
/**
- * Changes the text with an animation. Makes sure a single indication is shown long enough.
+ * Changes the text with an animation.
*/
public void switchIndication(CharSequence text, KeyguardIndication indication) {
switchIndication(text, indication, true, null);
}
/**
- * Changes the text with an optional animation. For animating text, makes sure a single
- * indication is shown long enough.
+ * Updates the text with an optional animation.
*
* @param text The text to show.
* @param indication optional display information for the text
@@ -112,33 +110,15 @@
*/
public void switchIndication(CharSequence text, KeyguardIndication indication,
boolean animate, Runnable onAnimationEndCallback) {
- if (text == null) text = "";
-
- CharSequence lastPendingMessage = mMessages.peekLast();
- if (TextUtils.equals(lastPendingMessage, text)
- || (lastPendingMessage == null && TextUtils.equals(text, getText()))) {
- if (onAnimationEndCallback != null) {
- onAnimationEndCallback.run();
- }
- return;
- }
- mMessages.add(text);
- mKeyguardIndicationInfo.add(indication);
+ mMessage = text;
+ mKeyguardIndicationInfo = indication;
if (animate) {
final boolean hasIcon = indication != null && indication.getIcon() != null;
- final AnimatorSet animator = new AnimatorSet();
+ AnimatorSet animator = new AnimatorSet();
// Make sure each animation is visible for a minimum amount of time, while not worrying
// about fading in blank text
- long timeInMillis = System.currentTimeMillis();
- long delay = Math.max(0, mNextAnimationTime - timeInMillis);
- setNextAnimationTime(timeInMillis + delay + getFadeOutDuration());
- final long minDurationMillis =
- (indication != null && indication.getMinVisibilityMillis() != null)
- ? indication.getMinVisibilityMillis()
- : MSG_MIN_DURATION_MILLIS_DEFAULT;
- if (!text.equals("") || hasIcon) {
- setNextAnimationTime(mNextAnimationTime + minDurationMillis);
+ if (!TextUtils.isEmpty(mMessage) || hasIcon) {
Animator inAnimator = getInAnimator();
inAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -164,7 +144,10 @@
animator.play(outAnimator);
}
- animator.setStartDelay(delay);
+ if (mLastAnimator != null) {
+ mLastAnimator.cancel();
+ }
+ mLastAnimator = animator;
animator.start();
} else {
setAlpha(1f);
@@ -173,6 +156,10 @@
if (onAnimationEndCallback != null) {
onAnimationEndCallback.run();
}
+ if (mLastAnimator != null) {
+ mLastAnimator.cancel();
+ mLastAnimator = null;
+ }
}
}
@@ -182,10 +169,20 @@
fadeOut.setDuration(getFadeOutDuration());
fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
fadeOut.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled = false;
@Override
public void onAnimationEnd(Animator animator) {
super.onAnimationEnd(animator);
- setNextIndication();
+ if (!mCancelled) {
+ setNextIndication();
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ super.onAnimationCancel(animator);
+ mCancelled = true;
+ setAlpha(0);
}
});
@@ -198,20 +195,19 @@
}
private void setNextIndication() {
- KeyguardIndication info = mKeyguardIndicationInfo.poll();
- if (info != null) {
+ if (mKeyguardIndicationInfo != null) {
// First, update the style.
// If a background is set on the text, we don't want shadow on the text
- if (info.getBackground() != null) {
+ if (mKeyguardIndicationInfo.getBackground() != null) {
setTextAppearance(sButtonStyleId);
} else {
setTextAppearance(sStyleId);
}
- setBackground(info.getBackground());
- setTextColor(info.getTextColor());
- setOnClickListener(info.getClickListener());
- setClickable(info.getClickListener() != null);
- final Drawable icon = info.getIcon();
+ setBackground(mKeyguardIndicationInfo.getBackground());
+ setTextColor(mKeyguardIndicationInfo.getTextColor());
+ setOnClickListener(mKeyguardIndicationInfo.getClickListener());
+ setClickable(mKeyguardIndicationInfo.getClickListener() != null);
+ final Drawable icon = mKeyguardIndicationInfo.getIcon();
if (icon != null) {
icon.setTint(getCurrentTextColor());
if (icon instanceof AnimatedVectorDrawable) {
@@ -220,7 +216,7 @@
}
setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
}
- setText(mMessages.poll());
+ setText(mMessage);
}
private AnimatorSet getInAnimator() {
@@ -238,6 +234,7 @@
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
setTranslationY(0);
+ setAlpha(1f);
}
});
animatorSet.playTogether(yTranslate, fadeIn);
@@ -270,14 +267,6 @@
return 167L;
}
- private void setNextAnimationTime(long time) {
- if (mAnimationsEnabled) {
- mNextAnimationTime = time;
- } else {
- mNextAnimationTime = 0L;
- }
- }
-
private int getYTranslationPixels() {
return mContext.getResources().getDimensionPixelSize(
com.android.systemui.R.dimen.keyguard_indication_y_translation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 570b0ca..88ae0db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -37,7 +37,6 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.policy.BatteryController;
import java.io.FileDescriptor;
@@ -251,7 +250,7 @@
private void updateNavigation() {
if (mNavigationBarController != null
- && !QuickStepContract.isGesturalMode(mNavigationMode)) {
+ && mNavigationBarController.supportsIconTintForNavMode(mNavigationMode)) {
mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 9021b74..415fb92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -28,6 +28,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -230,6 +231,14 @@
}
/**
+ * Return whether to use the tint calculated in this class for nav icons.
+ */
+ public boolean supportsIconTintForNavMode(int navigationMode) {
+ // In gesture mode, we already do region sampling to update tint based on content beneath.
+ return !QuickStepContract.isGesturalMode(navigationMode);
+ }
+
+ /**
* Interface to apply a specific dark intensity.
*/
public interface DarkIntensityApplier {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 3f33281..68ab077 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -124,6 +124,9 @@
public void onAnimationEnd(Animator a) {
mLightsOutNotifView.setAlpha(showDot ? 1 : 0);
mLightsOutNotifView.setVisibility(showDot ? View.VISIBLE : View.GONE);
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ mLightsOutNotifView.animate().setListener(null);
}
})
.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 9b5fa2a..33c4109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -88,6 +88,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
@@ -109,6 +110,7 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
@@ -224,7 +226,8 @@
*/
private static final int FLING_HIDE = 2;
private static final long ANIMATION_DELAY_ICON_FADE_IN =
- LaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
+ ActivityLaunchAnimator.TIMINGS.getTotalDuration()
+ - CollapsedStatusBarFragment.FADE_IN_DURATION
- CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
private final DozeParameters mDozeParameters;
@@ -451,9 +454,13 @@
private boolean mUserSetupComplete;
private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass;
- private ArrayList<Consumer<ExpandableNotificationRow>>
- mTrackingHeadsUpListeners =
- new ArrayList<>();
+ /**
+ * Non-null if there's a heads-up notification that we're currently tracking the position of.
+ */
+ @Nullable
+ private ExpandableNotificationRow mTrackedHeadsUpNotification;
+ private final ArrayList<Consumer<ExpandableNotificationRow>>
+ mTrackingHeadsUpListeners = new ArrayList<>();
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private int mPanelAlpha;
@@ -1296,8 +1303,11 @@
mKeyguardStatusViewController.displayClock(LARGE);
}
updateKeyguardStatusViewAlignment(true /* animate */);
- int userIconHeight = mKeyguardQsUserSwitchController != null
+ int userSwitcherHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
+ if (mKeyguardUserSwitcherController != null) {
+ userSwitcherHeight = mKeyguardUserSwitcherController.getHeight();
+ }
float expandedFraction =
mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
? 1.0f : getExpandedFraction();
@@ -1317,7 +1327,7 @@
mStatusBarHeaderHeightKeyguard,
expandedFraction,
mKeyguardStatusViewController.getLockscreenHeight(),
- userIconHeight,
+ userSwitcherHeight,
userSwitcherPreferredY,
darkamount, mOverStretchAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
@@ -2241,11 +2251,14 @@
private void updateQsExpansion() {
if (mQs == null) return;
- float qsExpansionFraction = computeQsExpansionFraction();
- float squishiness = mNotificationStackScrollLayoutController
- .getNotificationSquishinessFraction();
- mQs.setQsExpansion(qsExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
- mQsExpandImmediate || mQsExpanded ? 1f : squishiness);
+ final float squishiness =
+ mQsExpandImmediate || mQsExpanded ? 1f : mNotificationStackScrollLayoutController
+ .getNotificationSquishinessFraction();
+ final float qsExpansionFraction = computeQsExpansionFraction();
+ final float adjustedExpansionFraction = mShouldUseSplitNotificationShade
+ ? 1f : computeQsExpansionFraction();
+ mQs.setQsExpansion(adjustedExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
+ squishiness);
mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
@@ -3050,18 +3063,24 @@
mQsExpandImmediate = false;
mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false);
mTwoFingerQsExpandPossible = false;
- notifyListenersTrackingHeadsUp(null);
+ updateTrackingHeadsUp(null);
mExpandingFromHeadsUp = false;
setPanelScrimMinFraction(0.0f);
}
- private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+ private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) {
+ mTrackedHeadsUpNotification = pickedChild;
for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
listener.accept(pickedChild);
}
}
+ @Nullable
+ public ExpandableNotificationRow getTrackedHeadsUpNotification() {
+ return mTrackedHeadsUpNotification;
+ }
+
private void setListening(boolean listening) {
mKeyguardStatusBarViewController.setBatteryListening(listening);
if (mQs == null) return;
@@ -3246,12 +3265,6 @@
mStatusBarStateController.setState(KEYGUARD);
}
return true;
- case StatusBarState.SHADE:
-
- // This gets called in the middle of the touch handling, where the state is still
- // that we are tracking the panel. Collapse the panel after this is done.
- mView.post(mPostCollapseRunnable);
- return false;
default:
return true;
}
@@ -3298,7 +3311,7 @@
public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
if (pickedChild != null) {
- notifyListenersTrackingHeadsUp(pickedChild);
+ updateTrackingHeadsUp(pickedChild);
mExpandingFromHeadsUp = true;
}
// otherwise we update the state when the expansion is finished
@@ -3615,8 +3628,8 @@
}
public void applyLaunchAnimationProgress(float linearProgress) {
- boolean hideIcons = LaunchAnimator.getProgress(linearProgress,
- ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
+ boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
+ linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
if (hideIcons != mHideIconsDuringLaunchAnimation) {
mHideIconsDuringLaunchAnimation = hideIcons;
if (!hideIcons) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 01587f7..55f1450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -338,6 +338,11 @@
return true;
}
+ if (mLockIconViewController.onInterceptTouchEvent(ev)) {
+ // immediately return true; don't send the touch to the drag down helper
+ return true;
+ }
+
boolean intercept = false;
if (mNotificationPanelViewController.isFullyExpanded()
&& mDragDownHelper.isDragDownEnabled()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 2823d98..2bf16fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -928,7 +928,6 @@
private void abortAnimations() {
cancelHeightAnimator();
- mView.removeCallbacks(mPostCollapseRunnable);
mView.removeCallbacks(mFlingCollapseRunnable);
}
@@ -1105,13 +1104,6 @@
return onMiddleClicked();
}
- protected final Runnable mPostCollapseRunnable = new Runnable() {
- @Override
- public void run() {
- collapse(false /* delayed */, 1.0f /* speedUpFactor */);
- }
- };
-
protected abstract boolean onMiddleClicked();
protected abstract boolean isDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 256b069..ec7e93b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -43,7 +43,6 @@
val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
- val viewCenterProvider = StatusBarViewsCenterProvider()
val viewsToAnimate = arrayOf(
statusBarLeftSide,
systemIconArea
@@ -52,7 +51,7 @@
mView.viewTreeObserver.addOnPreDrawListener(object :
ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
- animationController.onViewsReady(viewsToAnimate, viewCenterProvider)
+ animationController.onViewsReady(viewsToAnimate)
mView.viewTreeObserver.removeOnPreDrawListener(this)
return true
}
@@ -82,7 +81,7 @@
mView.importantForAccessibility = mode
}
- private class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
+ class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
override fun getViewCenter(view: View, outPoint: Point) =
when (view.id) {
R.id.status_bar_left_side -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 9cefded..bf54677 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -120,6 +120,9 @@
setAlpha(1f);
setTranslationX(0);
cancelLongClick();
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ animate().setListener(null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index c814622..401c1b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -23,8 +23,10 @@
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
import javax.inject.Inject
import javax.inject.Named
@@ -33,9 +35,10 @@
class SplitShadeHeaderController @Inject constructor(
@Named(SPLIT_SHADE_HEADER) private val statusBar: View,
private val statusBarIconController: StatusBarIconController,
+ private val privacyIconsController: HeaderPrivacyIconsController,
qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
featureFlags: FeatureFlags,
- batteryMeterViewController: BatteryMeterViewController
+ @Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController
) {
companion object {
@@ -43,10 +46,11 @@
private val SPLIT_HEADER_TRANSITION_ID = R.id.split_header_transition
}
+ private val carrierIconSlots: List<String>
private val combinedHeaders = featureFlags.useCombinedQSHeaders()
- // TODO(b/194178072) Handle RSSI hiding when multi carrier
private val iconManager: StatusBarIconController.IconManager
private val qsCarrierGroupController: QSCarrierGroupController
+ private val iconContainer: StatusIconContainer
private var visible = false
set(value) {
if (field == value) {
@@ -62,8 +66,7 @@
return
}
field = value
- updateVisibility()
- updatePosition()
+ onShadeExpandedChanged()
}
var splitShadeMode = false
@@ -72,8 +75,7 @@
return
}
field = value
- updateVisibility()
- updateConstraints()
+ onSplitShadeModeChanged()
}
var shadeExpandedFraction = -1f
@@ -114,7 +116,16 @@
batteryMeterViewController.ignoreTunerUpdates()
batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
- val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
+ carrierIconSlots = if (featureFlags.isCombinedStatusBarSignalIconsEnabled) {
+ listOf(
+ statusBar.context.getString(com.android.internal.R.string.status_bar_no_calling),
+ statusBar.context.getString(com.android.internal.R.string.status_bar_call_strength)
+ )
+ } else {
+ listOf(statusBar.context.getString(com.android.internal.R.string.status_bar_mobile))
+ }
+
+ iconContainer = statusBar.findViewById(R.id.statusIcons)
iconManager = StatusBarIconController.IconManager(iconContainer, featureFlags)
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
@@ -123,6 +134,26 @@
updateConstraints()
}
+ private fun onShadeExpandedChanged() {
+ if (shadeExpanded) {
+ privacyIconsController.startListening()
+ } else {
+ privacyIconsController.stopListening()
+ }
+ updateVisibility()
+ updatePosition()
+ }
+
+ private fun onSplitShadeModeChanged() {
+ if (splitShadeMode) {
+ privacyIconsController.onParentVisible()
+ } else {
+ privacyIconsController.onParentInvisible()
+ }
+ updateVisibility()
+ updateConstraints()
+ }
+
private fun updateVisibility() {
val visibility = if (!splitShadeMode && !combinedHeaders) {
View.GONE
@@ -160,9 +191,20 @@
private fun updateListeners() {
qsCarrierGroupController.setListening(visible)
if (visible) {
+ updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
+ qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
statusBarIconController.addIconGroup(iconManager)
} else {
+ qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
statusBarIconController.removeIconGroup(iconManager)
}
}
-}
\ No newline at end of file
+
+ private fun updateSingleCarrier(singleCarrier: Boolean) {
+ if (singleCarrier) {
+ iconContainer.removeIgnoredSlots(carrierIconSlots)
+ } else {
+ iconContainer.addIgnoredSlots(carrierIconSlots)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e033c47..7a68b96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -133,6 +133,7 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.assist.AssistManager;
@@ -150,6 +151,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -489,7 +491,6 @@
private final DozeParameters mDozeParameters;
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
private final StatusBarComponent.Factory mStatusBarComponentFactory;
- private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
private final PluginManager mPluginManager;
private final Optional<LegacySplitScreen> mSplitScreenOptional;
private final StatusBarNotificationActivityStarter.Builder
@@ -519,7 +520,6 @@
private QSPanelController mQSPanelController;
private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
- private final PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
KeyguardIndicationController mKeyguardIndicationController;
private View mReportRejectedTouch;
@@ -538,7 +538,7 @@
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSliderController.Factory mBrightnessSliderFactory;
private final FeatureFlags mFeatureFlags;
-
+ private final FragmentService mFragmentService;
private final WallpaperController mWallpaperController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final MessageRouter mMessageRouter;
@@ -546,6 +546,8 @@
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final TunerService mTunerService;
+ private StatusBarComponent mStatusBarComponent;
+
// Flags for disabling the status bar
// Two variables becaseu the first one evidently ran out of room for new flags.
private int mDisabled1 = 0;
@@ -662,7 +664,6 @@
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
- private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
protected StatusBarNotificationPresenter mPresenter;
@@ -690,6 +691,7 @@
public StatusBar(
Context context,
NotificationsController notificationsController,
+ FragmentService fragmentService,
LightBarController lightBarController,
AutoHideController autoHideController,
StatusBarWindowController statusBarWindowController,
@@ -731,6 +733,7 @@
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
+ AccessibilityFloatingMenuController accessibilityFloatingMenuController,
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
@@ -747,7 +750,6 @@
CommandQueue commandQueue,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
StatusBarComponent.Factory statusBarComponentFactory,
- StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
PluginManager pluginManager,
Optional<LegacySplitScreen> splitScreenOptional,
LightsOutNotifController lightsOutNotifController,
@@ -763,7 +765,6 @@
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
- PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
@@ -791,6 +792,7 @@
ActivityLaunchAnimator activityLaunchAnimator) {
super(context);
mNotificationsController = notificationsController;
+ mFragmentService = fragmentService;
mLightBarController = lightBarController;
mAutoHideController = autoHideController;
mStatusBarWindowController = statusBarWindowController;
@@ -801,7 +803,6 @@
mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManagerPhone;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
- mPhoneStatusBarViewControllerFactory = phoneStatusBarViewControllerFactory;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mDynamicPrivacyController = dynamicPrivacyController;
@@ -835,6 +836,7 @@
mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mNavigationBarController = navigationBarController;
+ mAccessibilityFloatingMenuController = accessibilityFloatingMenuController;
mAssistManagerLazy = assistManagerLazy;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -852,7 +854,6 @@
mCommandQueue = commandQueue;
mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mStatusBarComponentFactory = statusBarComponentFactory;
- mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
mPluginManager = pluginManager;
mSplitScreenOptional = splitScreenOptional;
mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
@@ -1050,6 +1051,8 @@
mBatteryController.observe(mLifecycle, mBatteryStateChangeCallback);
mLifecycle.setCurrentState(RESUMED);
+ mAccessibilityFloatingMenuController.init();
+
// set the initial view visibility
int disabledFlags1 = result.mDisabledFlags1;
int disabledFlags2 = result.mDisabledFlags2;
@@ -1147,12 +1150,8 @@
}
mStatusBarView = statusBarFragmentComponent.getPhoneStatusBarView();
-
- // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
- mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
- .create(mStatusBarView, mNotificationPanelViewController
- .getStatusBarTouchEventHandler());
- mPhoneStatusBarViewController.init();
+ mPhoneStatusBarViewController =
+ statusBarFragmentComponent.getPhoneStatusBarViewController();
// Ensure we re-propagate panel expansion values to the panel controller and
// any listeners it may have, such as PanelBar. This will also ensure we
@@ -1162,21 +1161,6 @@
mNotificationPanelViewController.updatePanelExpansionAndVisibility();
setBouncerShowingForStatusBarComponents(mBouncerShowing);
- HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
- if (mHeadsUpAppearanceController != null) {
- // This view is being recreated, let's destroy the old one
- mHeadsUpAppearanceController.destroy();
- }
- // TODO (b/136993073) Separate notification shade and status bar
- // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
- mHeadsUpAppearanceController = new HeadsUpAppearanceController(
- mNotificationIconAreaController, mHeadsUpManager,
- mStackScrollerController,
- mStatusBarStateController, mKeyguardBypassController,
- mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
- mNotificationPanelViewController, mStatusBarView);
- mHeadsUpAppearanceController.readFrom(oldController);
-
mLightsOutNotifController.setLightsOutNotifView(
mStatusBarView.findViewById(R.id.notification_lights_out));
mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
@@ -1184,24 +1168,7 @@
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container,
- new CollapsedStatusBarFragment(
- mStatusBarFragmentComponentFactory,
- mOngoingCallController,
- mAnimationScheduler,
- mStatusBarLocationPublisher,
- mNotificationIconAreaController,
- mPanelExpansionStateManager,
- mFeatureFlags,
- () -> Optional.of(this),
- mStatusBarIconController,
- mStatusBarHideIconsForBouncerManager,
- mKeyguardStateController,
- mNetworkController,
- mStatusBarStateController,
- mCommandQueue,
- mCollapsedStatusBarFragmentLogger,
- mOperatorNameViewControllerFactory
- ),
+ mStatusBarComponent.createCollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
@@ -1557,32 +1524,34 @@
}
private void inflateStatusBarWindow() {
- StatusBarComponent statusBarComponent = mStatusBarComponentFactory.create();
- mNotificationShadeWindowView = statusBarComponent.getNotificationShadeWindowView();
- mNotificationShadeWindowViewController = statusBarComponent
+ mStatusBarComponent = mStatusBarComponentFactory.create();
+ mFragmentService.addFragmentInstantiationProvider(mStatusBarComponent);
+
+ mNotificationShadeWindowView = mStatusBarComponent.getNotificationShadeWindowView();
+ mNotificationShadeWindowViewController = mStatusBarComponent
.getNotificationShadeWindowViewController();
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
- mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
- statusBarComponent.getLockIconViewController().init();
- mStackScrollerController = statusBarComponent.getNotificationStackScrollLayoutController();
+ mNotificationPanelViewController = mStatusBarComponent.getNotificationPanelViewController();
+ mStatusBarComponent.getLockIconViewController().init();
+ mStackScrollerController = mStatusBarComponent.getNotificationStackScrollLayoutController();
mStackScroller = mStackScrollerController.getView();
- mNotificationShelfController = statusBarComponent.getNotificationShelfController();
- mAuthRippleController = statusBarComponent.getAuthRippleController();
+ mNotificationShelfController = mStatusBarComponent.getNotificationShelfController();
+ mAuthRippleController = mStatusBarComponent.getAuthRippleController();
mAuthRippleController.init();
- mHeadsUpManager.addListener(statusBarComponent.getStatusBarHeadsUpChangeListener());
+ mHeadsUpManager.addListener(mStatusBarComponent.getStatusBarHeadsUpChangeListener());
- mHeadsUpManager.addListener(statusBarComponent.getStatusBarHeadsUpChangeListener());
+ mHeadsUpManager.addListener(mStatusBarComponent.getStatusBarHeadsUpChangeListener());
// Listen for demo mode changes
- mDemoModeController.addCallback(statusBarComponent.getStatusBarDemoMode());
+ mDemoModeController.addCallback(mStatusBarComponent.getStatusBarDemoMode());
if (mCommandQueueCallbacks != null) {
mCommandQueue.removeCallback(mCommandQueueCallbacks);
}
- mCommandQueueCallbacks = statusBarComponent.getStatusBarCommandQueueCallbacks();
+ mCommandQueueCallbacks = mStatusBarComponent.getStatusBarCommandQueueCallbacks();
// Connect in to the status bar manager service
mCommandQueue.addCallback(mCommandQueueCallbacks);
}
@@ -1892,10 +1861,6 @@
return mDozeServiceHost.isPulsing();
}
- public boolean hideStatusBarIconsWhenExpanded() {
- return mNotificationPanelViewController.hideStatusBarIconsWhenExpanded();
- }
-
@Nullable
public View getAmbientIndicationContainer() {
return mAmbientIndicationContainer;
@@ -1917,10 +1882,6 @@
mScrimController.setKeyguardOccluded(occluded);
}
- public boolean headsUpShouldBeVisible() {
- return mHeadsUpAppearanceController.shouldBeVisible();
- }
-
/** A launch animation was cancelled. */
//TODO: These can / should probably be moved to NotificationPresenter or ShadeController
public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
@@ -2000,7 +1961,7 @@
}
}
- public void maybeEscalateHeadsUp() {
+ private void maybeEscalateHeadsUp() {
mHeadsUpManager.getAllEntries().forEach(entry -> {
final StatusBarNotification sbn = entry.getSbn();
final Notification notification = sbn.getNotification();
@@ -2011,6 +1972,7 @@
try {
EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
sbn.getKey());
+ wakeUpForFullScreenIntent();
notification.fullScreenIntent.send();
entry.notifyFullScreenIntentLaunched();
} catch (PendingIntent.CanceledException e) {
@@ -2020,6 +1982,17 @@
mHeadsUpManager.releaseAllImmediately();
}
+ void wakeUpForFullScreenIntent() {
+ if (isGoingToSleep() || mDozing) {
+ mPowerManager.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION,
+ "com.android.systemui:full_screen_intent");
+ mWakeUpComingFromTouch = false;
+ mWakeUpTouchLocation = null;
+ }
+ }
+
void makeExpandedVisible(boolean force) {
if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
@@ -2109,7 +2082,7 @@
mShadeController.runPostCollapseRunnables();
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
- showBouncerIfKeyguard();
+ showBouncerOrLockScreenIfKeyguard();
} else if (DEBUG) {
Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
}
@@ -3217,7 +3190,7 @@
boolean wakeAndUnlock = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
- || (mDozing && mDozeServiceHost.shouldAnimateScreenOff()
+ || (mDozing && mDozeParameters.shouldControlScreenOff()
&& visibleNotOccludedOrWillBe);
mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
@@ -3315,13 +3288,17 @@
return false;
}
- private void showBouncerIfKeyguard() {
+ private void showBouncerOrLockScreenIfKeyguard() {
if (!mKeyguardViewMediator.isHiding()) {
- if (mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) {
+ if (mState == StatusBarState.SHADE_LOCKED
+ && mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
+ // shade is showing while locked on the keyguard, so go back to showing the
+ // lock screen where users can use the UDFPS affordance to enter the device
+ mStatusBarKeyguardViewManager.reset(true);
+ } else if ((mState == StatusBarState.KEYGUARD
+ && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing())
+ || mState == StatusBarState.SHADE_LOCKED) {
mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
- } else if (mState == StatusBarState.SHADE_LOCKED) {
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
}
}
}
@@ -3456,6 +3433,14 @@
return mNavigationBarController.getNavigationBarView(mDisplayId);
}
+ public void showPinningEnterExitToast(boolean entering) {
+ mNavigationBarController.showPinningEnterExitToast(mDisplayId, entering);
+ }
+
+ public void showPinningEscapeToast() {
+ mNavigationBarController.showPinningEscapeToast(mDisplayId);
+ }
+
/**
* TODO: Remove this method. Views should not be passed forward. Will cause theme issues.
* @return bottom area view
@@ -3549,7 +3534,7 @@
DejankUtils.startDetectingBlockingIpcs(tag);
updateRevealEffect(false /* wakingUp */);
updateNotificationPanelTouchState();
- notifyHeadsUpGoingToSleep();
+ maybeEscalateHeadsUp();
dismissVolumeDialog();
mWakeUpCoordinator.setFullyAwake(false);
mBypassHeadsUpNotifier.setFullyAwake(false);
@@ -3835,6 +3820,7 @@
private final DeviceProvisionedController mDeviceProvisionedController;
private final NavigationBarController mNavigationBarController;
+ private final AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
// UI-specific methods
@@ -4094,10 +4080,6 @@
}
}
- protected void notifyHeadsUpGoingToSleep() {
- maybeEscalateHeadsUp();
- }
-
/**
* @return Whether the security bouncer from Keyguard is showing.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index a77a097..3ce4472 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -549,16 +549,12 @@
@Override
public void showPinningEnterExitToast(boolean entering) {
- if (mStatusBar.getNavigationBarView() != null) {
- mStatusBar.getNavigationBarView().showPinningEnterExitToast(entering);
- }
+ mStatusBar.showPinningEnterExitToast(entering);
}
@Override
public void showPinningEscapeToast() {
- if (mStatusBar.getNavigationBarView() != null) {
- mStatusBar.getNavigationBarView().showPinningEscapeToast();
- }
+ mStatusBar.showPinningEscapeToast();
}
@Override
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 7ab4a1e..e788928 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
@@ -337,6 +338,12 @@
// • Full-screen user switcher is displayed.
if (mNotificationPanelViewController.isUnlockHintRunning()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+ && mKeyguardUpdateManager.isUdfpsEnrolled()) {
+ // Don't expand to the bouncer. Instead transition back to the lock screen (see
+ // StatusBar#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS
+ // affordance to enter the device (or swipe up to the input bouncer)
+ return;
} else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing) {
@@ -409,7 +416,7 @@
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
public void showGenericBouncer(boolean scrimmed) {
- if (mAlternateAuthInterceptor != null) {
+ if (shouldShowAltAuth()) {
updateAlternateAuthShowing(mAlternateAuthInterceptor.showAlternateAuthBouncer());
return;
}
@@ -417,6 +424,11 @@
showBouncer(scrimmed);
}
+ private boolean shouldShowAltAuth() {
+ return mAlternateAuthInterceptor != null
+ && mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
+ }
+
/**
* Hides the input bouncer (pin/password/pattern).
*/
@@ -441,6 +453,8 @@
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
public void showBouncer(boolean scrimmed) {
+ resetAlternateAuth(false);
+
if (mShowing && !mBouncer.isShowing()) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
}
@@ -470,7 +484,7 @@
// If there is an an alternate auth interceptor (like the UDFPS), show that one instead
// of the bouncer.
- if (mAlternateAuthInterceptor != null) {
+ if (shouldShowAltAuth()) {
if (!afterKeyguardGone) {
mBouncer.setDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
mAfterKeyguardGoneAction = null;
@@ -553,12 +567,13 @@
public void onStartedWakingUp() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(false);
- View currentView = getCurrentNavBarView();
- if (currentView != null) {
- currentView.animate()
- .alpha(1f)
- .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
- .start();
+ NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ if (navBarView != null) {
+ navBarView.forEachView(view ->
+ view.animate()
+ .alpha(1f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start());
}
}
@@ -566,12 +581,13 @@
public void onStartedGoingToSleep() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(true);
- View currentView = getCurrentNavBarView();
- if (currentView != null) {
- currentView.animate()
- .alpha(0f)
- .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
- .start();
+ NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ if (navBarView != null) {
+ navBarView.forEachView(view ->
+ view.animate()
+ .alpha(0f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start());
}
}
@@ -1013,17 +1029,6 @@
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
- /**
- * Updates the visibility of the nav bar content views.
- */
- private void updateNavigationBarContentVisibility(boolean navBarContentVisible) {
- final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
- if (navBarView != null && navBarView.getCurrentView() != null) {
- final View currentView = navBarView.getCurrentView();
- currentView.setVisibility(navBarContentVisible ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
private View getCurrentNavBarView() {
final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
return navBarView != null ? navBarView.getCurrentView() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index 32aae6c..2ba37c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -23,7 +23,8 @@
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(true)
if (!isExpandingFullyAbove) {
- statusBar.collapsePanelWithDuration(LaunchAnimator.ANIMATION_DURATION.toInt())
+ statusBar.collapsePanelWithDuration(
+ ActivityLaunchAnimator.TIMINGS.totalDuration.toInt())
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
index 805ddf5..6d033477 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -18,7 +18,7 @@
import android.view.View
import android.view.WindowManager
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
-import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.ViewCenterProvider
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController.StatusBarViewsCenterProvider
import com.android.systemui.unfold.SysUIUnfoldScope
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -27,20 +27,18 @@
@SysUIUnfoldScope
class StatusBarMoveFromCenterAnimationController @Inject constructor(
private val progressProvider: ScopedUnfoldTransitionProgressProvider,
- private val windowManager: WindowManager
+ windowManager: WindowManager
) {
private val transitionListener = TransitionListener()
- private var moveFromCenterAnimator: UnfoldMoveFromCenterAnimator? = null
+ private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
+ viewCenterProvider = StatusBarViewsCenterProvider())
- fun onViewsReady(viewsToAnimate: Array<View>, viewCenterProvider: ViewCenterProvider) {
- moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
- viewCenterProvider = viewCenterProvider)
-
- moveFromCenterAnimator?.updateDisplayProperties()
+ fun onViewsReady(viewsToAnimate: Array<View>) {
+ moveFromCenterAnimator.updateDisplayProperties()
viewsToAnimate.forEach {
- moveFromCenterAnimator?.registerViewForAnimation(it)
+ moveFromCenterAnimator.registerViewForAnimation(it)
}
progressProvider.addCallback(transitionListener)
@@ -48,24 +46,23 @@
fun onViewDetached() {
progressProvider.removeCallback(transitionListener)
- moveFromCenterAnimator?.clearRegisteredViews()
- moveFromCenterAnimator = null
+ moveFromCenterAnimator.clearRegisteredViews()
}
fun onStatusBarWidthChanged() {
- moveFromCenterAnimator?.updateDisplayProperties()
- moveFromCenterAnimator?.updateViewPositions()
+ moveFromCenterAnimator.updateDisplayProperties()
+ moveFromCenterAnimator.updateViewPositions()
}
private inner class TransitionListener : TransitionProgressListener {
override fun onTransitionProgress(progress: Float) {
- moveFromCenterAnimator?.onTransitionProgress(progress)
+ moveFromCenterAnimator.onTransitionProgress(progress)
}
override fun onTransitionFinished() {
// Reset translations when transition is stopped/cancelled
// (e.g. the transition could be cancelled mid-way when rotating the screen)
- moveFromCenterAnimator?.onTransitionProgress(1f)
+ moveFromCenterAnimator.onTransitionProgress(1f)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index dba3b24..ce38111 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -41,6 +41,8 @@
import android.util.EventLog;
import android.view.View;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
@@ -593,7 +595,8 @@
}
}
- private void handleFullScreenIntent(NotificationEntry entry) {
+ @VisibleForTesting
+ void handleFullScreenIntent(NotificationEntry entry) {
if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
if (shouldSuppressFullScreenIntent(entry)) {
mLogger.logFullScreenIntentSuppressedByDnD(entry.getKey());
@@ -617,6 +620,7 @@
try {
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
entry.getKey());
+ mStatusBar.wakeUpForFullScreenIntent();
fullscreenIntent.send();
entry.notifyFullScreenIntentLaunched();
mMetricsLogger.count("note_fullscreen", 1);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 1130ec2..93f9892 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -36,33 +36,31 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import androidx.annotation.Nullable;
+
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.animation.DialogListener;
-import com.android.systemui.animation.DialogListener.DismissReason;
-import com.android.systemui.animation.ListenableDialog;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-
/**
* Base class for dialogs that should appear over panels and keyguard.
+ *
+ * Optionally provide a {@link SystemUIDialogManager} to its constructor to send signals to
+ * listeners on whether this dialog is showing.
+ *
* The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
* and dismisses itself when it receives the broadcast.
*/
-public class SystemUIDialog extends AlertDialog implements ListenableDialog,
- ViewRootImpl.ConfigChangedCallback {
+public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigChangedCallback {
// TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on.
private static final String FLAG_TABLET_DIALOG_WIDTH =
"persist.systemui.flag_tablet_dialog_width";
private final Context mContext;
- private final DismissReceiver mDismissReceiver;
- private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>();
+ @Nullable private final DismissReceiver mDismissReceiver;
private final Handler mHandler = new Handler();
+ @Nullable private final SystemUIDialogManager mDialogManager;
private int mLastWidth = Integer.MIN_VALUE;
private int mLastHeight = Integer.MIN_VALUE;
@@ -73,11 +71,22 @@
this(context, R.style.Theme_SystemUI_Dialog);
}
+ public SystemUIDialog(Context context, SystemUIDialogManager dialogManager) {
+ this(context, R.style.Theme_SystemUI_Dialog, true, dialogManager);
+ }
+
public SystemUIDialog(Context context, int theme) {
this(context, theme, true /* dismissOnDeviceLock */);
}
-
public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
+ this(context, theme, dismissOnDeviceLock, null);
+ }
+
+ /**
+ * @param udfpsDialogManager If set, UDFPS will hide if this dialog is showing.
+ */
+ public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
+ SystemUIDialogManager dialogManager) {
super(context, theme);
mContext = context;
@@ -87,6 +96,7 @@
getWindow().setAttributes(attrs);
mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null;
+ mDialogManager = dialogManager;
}
@Override
@@ -115,10 +125,6 @@
mLastWidth = width;
mLastHeight = height;
getWindow().setLayout(width, height);
-
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onSizeChanged();
- }
}
@Override
@@ -137,30 +143,7 @@
* the device configuration changes, and the result will be used to resize this dialog window.
*/
protected int getWidth() {
- boolean isOnTablet =
- mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
- if (!isOnTablet) {
- return ViewGroup.LayoutParams.MATCH_PARENT;
- }
-
- int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0);
- if (flagValue == -1) {
- // The width of bottom sheets (624dp).
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624,
- mContext.getResources().getDisplayMetrics()));
- } else if (flagValue == -2) {
- // The suggested small width for all dialogs (348dp)
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348,
- mContext.getResources().getDisplayMetrics()));
- } else if (flagValue > 0) {
- // Any given width.
- return Math.round(
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue,
- mContext.getResources().getDisplayMetrics()));
- } else {
- // By default we use the same width as the notification shade in portrait mode (504dp).
- return mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
- }
+ return getDefaultDialogWidth(mContext);
}
/**
@@ -168,7 +151,7 @@
* the device configuration changes, and the result will be used to resize this dialog window.
*/
protected int getHeight() {
- return ViewGroup.LayoutParams.WRAP_CONTENT;
+ return getDefaultDialogHeight();
}
@Override
@@ -179,6 +162,10 @@
mDismissReceiver.register();
}
+ if (mDialogManager != null) {
+ mDialogManager.setShowing(this, true);
+ }
+
// Listen for configuration changes to resize this dialog window. This is mostly necessary
// for foldables that often go from large <=> small screen when folding/unfolding.
ViewRootImpl.addConfigCallback(this);
@@ -192,63 +179,13 @@
mDismissReceiver.unregister();
}
+ if (mDialogManager != null) {
+ mDialogManager.setShowing(this, false);
+ }
+
ViewRootImpl.removeConfigCallback(this);
}
- @Override
- public void addListener(DialogListener listener) {
- mDialogListeners.add(listener);
- }
-
- @Override
- public void removeListener(DialogListener listener) {
- mDialogListeners.remove(listener);
- }
-
- @Override
- public void dismiss() {
- dismiss(DismissReason.UNKNOWN);
- }
-
- private void dismiss(DismissReason reason) {
- super.dismiss();
-
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onDismiss(reason);
- }
- }
-
- /**
- * Dismiss this dialog. If it was launched from another dialog using
- * {@link com.android.systemui.animation.DialogLaunchAnimator#showFromView} with a
- * non-{@code null} {@code parentHostDialog} parameter, also dismisses the stack of dialogs,
- * animating back to the original touchSurface.
- */
- public void dismissStack() {
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.prepareForStackDismiss();
- }
- dismiss();
- }
-
- @Override
- public void hide() {
- super.hide();
-
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onHide();
- }
- }
-
- @Override
- public void show() {
- super.show();
-
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onShow();
- }
- }
-
public void setShowForAllUsers(boolean show) {
setShowForAllUsers(this, show);
}
@@ -303,16 +240,74 @@
* the screen off / close system dialogs broadcast.
* <p>
* <strong>Note:</strong> Don't call dialog.setOnDismissListener() after
- * calling this because it causes a leak of BroadcastReceiver.
+ * calling this because it causes a leak of BroadcastReceiver. Instead, call the version that
+ * takes an extra Runnable as a parameter.
*
* @param dialog The dialog to be associated with the listener.
*/
public static void registerDismissListener(Dialog dialog) {
+ registerDismissListener(dialog, null);
+ }
+
+
+ /**
+ * Registers a listener that dismisses the given dialog when it receives
+ * the screen off / close system dialogs broadcast.
+ * <p>
+ * <strong>Note:</strong> Don't call dialog.setOnDismissListener() after
+ * calling this because it causes a leak of BroadcastReceiver.
+ *
+ * @param dialog The dialog to be associated with the listener.
+ * @param dismissAction An action to run when the dialog is dismissed.
+ */
+ public static void registerDismissListener(Dialog dialog, @Nullable Runnable dismissAction) {
DismissReceiver dismissReceiver = new DismissReceiver(dialog);
- dialog.setOnDismissListener(d -> dismissReceiver.unregister());
+ dialog.setOnDismissListener(d -> {
+ dismissReceiver.unregister();
+ if (dismissAction != null) dismissAction.run();
+ });
dismissReceiver.register();
}
+ /** Set an appropriate size to {@code dialog} depending on the current configuration. */
+ public static void setDialogSize(Dialog dialog) {
+ // We need to create the dialog first, otherwise the size will be overridden when it is
+ // created.
+ dialog.create();
+ dialog.getWindow().setLayout(getDefaultDialogWidth(dialog.getContext()),
+ getDefaultDialogHeight());
+ }
+
+ private static int getDefaultDialogWidth(Context context) {
+ boolean isOnTablet = context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
+ if (!isOnTablet) {
+ return ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+
+ int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0);
+ if (flagValue == -1) {
+ // The width of bottom sheets (624dp).
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624,
+ context.getResources().getDisplayMetrics()));
+ } else if (flagValue == -2) {
+ // The suggested small width for all dialogs (348dp)
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348,
+ context.getResources().getDisplayMetrics()));
+ } else if (flagValue > 0) {
+ // Any given width.
+ return Math.round(
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue,
+ context.getResources().getDisplayMetrics()));
+ } else {
+ // By default we use the same width as the notification shade in portrait mode (504dp).
+ return context.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
+ }
+ }
+
+ private static int getDefaultDialogHeight() {
+ return ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
private static class DismissReceiver extends BroadcastReceiver {
private static final IntentFilter INTENT_FILTER = new IntentFilter();
static {
@@ -343,11 +338,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- if (mDialog instanceof SystemUIDialog) {
- ((SystemUIDialog) mDialog).dismiss(DismissReason.DEVICE_LOCKED);
- } else {
- mDialog.dismiss();
- }
+ mDialog.dismiss();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
new file mode 100644
index 0000000..204f710
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
@@ -0,0 +1,118 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+/**
+ * Register dialogs to this manager if extraneous affordances (like the UDFPS sensor area)
+ * should be hidden from the screen when the dialog shows.
+ *
+ * Currently, only used if UDFPS is supported on the device; however, can be extended in the future
+ * for other use cases.
+ */
+@SysUISingleton
+public class SystemUIDialogManager implements Dumpable {
+ private final StatusBarKeyguardViewManager mKeyguardViewManager;
+
+ private final Set<SystemUIDialog> mDialogsShowing = new HashSet<>();
+ private final Set<Listener> mListeners = new HashSet<>();
+
+ @Inject
+ public SystemUIDialogManager(
+ DumpManager dumpManager,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ dumpManager.registerDumpable(this);
+ mKeyguardViewManager = statusBarKeyguardViewManager;
+ }
+
+ /**
+ * Whether listeners should hide affordances like the UDFPS sensor icon.
+ */
+ public boolean shouldHideAffordance() {
+ return !mDialogsShowing.isEmpty();
+ }
+
+ /**
+ * Register a listener to receive callbacks.
+ */
+ public void registerListener(@NonNull Listener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Unregister a listener from receiving callbacks.
+ */
+ public void unregisterListener(@NonNull Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ void setShowing(SystemUIDialog dialog, boolean showing) {
+ final boolean wasHidingAffordances = shouldHideAffordance();
+ if (showing) {
+ mDialogsShowing.add(dialog);
+ } else {
+ mDialogsShowing.remove(dialog);
+ }
+
+ if (wasHidingAffordances != shouldHideAffordance()) {
+ updateDialogListeners();
+ }
+ }
+
+ private void updateDialogListeners() {
+ if (shouldHideAffordance()) {
+ mKeyguardViewManager.resetAlternateAuth(true);
+ }
+
+ for (Listener listener : mListeners) {
+ listener.shouldHideAffordances(shouldHideAffordance());
+ }
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("listeners:");
+ for (Listener listener : mListeners) {
+ pw.println("\t" + listener);
+ }
+ pw.println("dialogs tracked:");
+ for (SystemUIDialog dialog : mDialogsShowing) {
+ pw.println("\t" + dialog);
+ }
+ }
+
+ /** SystemUIDialogManagerListener */
+ public interface Listener {
+ /**
+ * Callback where shouldHide=true if listeners should hide their views that may overlap
+ * a showing dialog.
+ */
+ void shouldHideAffordances(boolean shouldHide);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
deleted file mode 100644
index 4f18f8c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.systemui.statusbar.phone
-
-import android.app.Dialog
-import android.content.Context
-import android.os.Bundle
-import android.view.ViewGroup
-import com.android.systemui.animation.HostDialogProvider
-
-/** An implementation of [HostDialogProvider] to be used when animating SysUI dialogs. */
-class SystemUIHostDialogProvider : HostDialogProvider {
- override fun createHostDialog(
- context: Context,
- theme: Int,
- onCreateCallback: () -> Unit,
- dismissOverride: (() -> Unit) -> Unit
- ): Dialog {
- return SystemUIHostDialog(context, theme, onCreateCallback, dismissOverride)
- }
-
- /**
- * This host dialog is a SystemUIDialog so that it's displayed above all SystemUI windows. Note
- * that it is not automatically dismissed when the device is locked, but only when the hosted
- * (original) dialog is dismissed. That way, the behavior of the dialog (dismissed when locking
- * or not) is consistent with when the dialog is shown with or without the dialog animator.
- */
- private class SystemUIHostDialog(
- context: Context,
- theme: Int,
- private val onCreateCallback: () -> Unit,
- private val dismissOverride: (() -> Unit) -> Unit
- ) : SystemUIDialog(context, theme, false /* dismissOnDeviceLock */) {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- onCreateCallback()
- }
-
- override fun dismiss() {
- dismissOverride {
- super.dismiss()
- }
- }
-
- override fun getWidth(): Int {
- return ViewGroup.LayoutParams.MATCH_PARENT
- }
-
- override fun getHeight(): Int {
- return ViewGroup.LayoutParams.MATCH_PARENT
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index cddde64..83ab8cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -226,10 +226,6 @@
return false
}
- if (!dozeParameters.get().canControlUnlockedScreenOff()) {
- return false
- }
-
// If animations are disabled system-wide, don't play this one either.
if (Settings.Global.getString(
context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE) == "0") {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index e06605e..375641f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -38,7 +39,13 @@
import dagger.Subcomponent;
/**
- * Dagger subcomponent tied to the lifecycle of StatusBar views.
+ * Dagger subcomponent for classes (semi-)related to the status bar. The component is created once
+ * inside {@link com.android.systemui.statusbar.phone.StatusBar} and never re-created.
+ *
+ * TODO(b/197137564): This should likely be re-factored a bit. It includes classes that aren't
+ * directly related to status bar functionality, like multiple notification classes. And, the fact
+ * that it has many getter methods indicates that we need to access many of these classes from
+ * outside the component. Should more items be moved *into* this component to avoid so many getters?
*/
@Subcomponent(modules = {StatusBarViewModule.class})
@StatusBarComponent.StatusBarScope
@@ -121,4 +128,10 @@
*/
@StatusBarScope
SplitShadeHeaderController getSplitShadeHeaderController();
+
+ /**
+ * Creates a new {@link CollapsedStatusBarFragment} each time it's called. See
+ * {@link StatusBarViewModule#createCollapsedStatusBarFragment}.
+ */
+ CollapsedStatusBarFragment createCollapsedStatusBarFragment();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 27ece84..fcb98fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -28,6 +28,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
+import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -39,6 +40,7 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -87,7 +89,6 @@
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -100,7 +101,6 @@
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -143,6 +143,7 @@
static StatusBar provideStatusBar(
Context context,
NotificationsController notificationsController,
+ FragmentService fragmentService,
LightBarController lightBarController,
AutoHideController autoHideController,
StatusBarWindowController statusBarWindowController,
@@ -184,6 +185,7 @@
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
+ AccessibilityFloatingMenuController accessibilityFloatingMenuController,
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
@@ -200,7 +202,6 @@
CommandQueue commandQueue,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
StatusBarComponent.Factory statusBarComponentFactory,
- StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
PluginManager pluginManager,
Optional<LegacySplitScreen> splitScreenOptional,
LightsOutNotifController lightsOutNotifController,
@@ -216,7 +217,6 @@
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
- PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
@@ -245,6 +245,7 @@
return new StatusBar(
context,
notificationsController,
+ fragmentService,
lightBarController,
autoHideController,
statusBarWindowController,
@@ -286,6 +287,7 @@
visualStabilityManager,
deviceProvisionedController,
navigationBarController,
+ accessibilityFloatingMenuController,
assistManagerLazy,
configurationController,
notificationShadeWindowController,
@@ -302,7 +304,6 @@
commandQueue,
collapsedStatusBarFragmentLogger,
statusBarComponentFactory,
- statusBarFragmentComponentFactory,
pluginManager,
splitScreenOptional,
lightsOutNotifController,
@@ -317,7 +318,6 @@
extensionController,
userInfoControllerImpl,
operatorNameViewControllerFactory,
- phoneStatusBarViewControllerFactory,
phoneStatusBarPolicy,
keyguardIndicationController,
demoModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 2765fe3..14cca13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.phone.dagger;
import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
@@ -24,26 +26,52 @@
import com.android.keyguard.LockIconView;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.biometrics.AuthRippleView;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.privacy.OngoingPrivacyChip;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.OperatorNameViewController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
+import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.phone.TapAgainView;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.tuner.TunerService;
import javax.inject.Named;
import dagger.Module;
import dagger.Provides;
-@Module
+@Module(subcomponents = StatusBarFragmentComponent.class)
public abstract class StatusBarViewModule {
public static final String SPLIT_SHADE_HEADER = "split_shade_header";
+ private static final String SPLIT_SHADE_BATTERY_VIEW = "split_shade_battery_view";
+ public static final String SPLIT_SHADE_BATTERY_CONTROLLER = "split_shade_battery_controller";
/** */
@Provides
@@ -143,10 +171,49 @@
/** */
@Provides
@StatusBarComponent.StatusBarScope
+ public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
+ @Named(SPLIT_SHADE_HEADER) View header) {
+ return header.findViewById(R.id.privacy_chip);
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ static StatusIconContainer providesStatusIconContainer(@Named(SPLIT_SHADE_HEADER) View header) {
+ return header.findViewById(R.id.statusIcons);
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ @Named(SPLIT_SHADE_BATTERY_VIEW)
static BatteryMeterView getBatteryMeterView(@Named(SPLIT_SHADE_HEADER) View view) {
return view.findViewById(R.id.batteryRemainingIcon);
}
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ @Named(SPLIT_SHADE_BATTERY_CONTROLLER)
+ static BatteryMeterViewController getBatteryMeterViewController(
+ @Named(SPLIT_SHADE_BATTERY_VIEW) BatteryMeterView batteryMeterView,
+ ConfigurationController configurationController,
+ TunerService tunerService,
+ BroadcastDispatcher broadcastDispatcher,
+ @Main Handler mainHandler,
+ ContentResolver contentResolver,
+ BatteryController batteryController
+ ) {
+ return new BatteryMeterViewController(
+ batteryMeterView,
+ configurationController,
+ tunerService,
+ broadcastDispatcher,
+ mainHandler,
+ contentResolver,
+ batteryController);
+
+ }
+
/** */
@Provides
@StatusBarComponent.StatusBarScope
@@ -161,4 +228,52 @@
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
}
+
+ /**
+ * Creates a new {@link CollapsedStatusBarFragment}.
+ *
+ * **IMPORTANT**: This method intentionally does not have
+ * {@link StatusBarComponent.StatusBarScope}, which means a new fragment *will* be created each
+ * time this method is called. This is intentional because we need fragments to re-created in
+ * certain lifecycle scenarios.
+ *
+ * **IMPORTANT**: This method also intentionally does not have a {@link Provides} annotation. If
+ * you need to get access to a {@link CollapsedStatusBarFragment}, go through
+ * {@link StatusBarFragmentComponent} instead.
+ */
+ public static CollapsedStatusBarFragment createCollapsedStatusBarFragment(
+ StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
+ OngoingCallController ongoingCallController,
+ SystemStatusAnimationScheduler animationScheduler,
+ StatusBarLocationPublisher locationPublisher,
+ NotificationIconAreaController notificationIconAreaController,
+ PanelExpansionStateManager panelExpansionStateManager,
+ FeatureFlags featureFlags,
+ StatusBarIconController statusBarIconController,
+ StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
+ KeyguardStateController keyguardStateController,
+ NotificationPanelViewController notificationPanelViewController,
+ NetworkController networkController,
+ StatusBarStateController statusBarStateController,
+ CommandQueue commandQueue,
+ CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
+ OperatorNameViewController.Factory operatorNameViewControllerFactory
+ ) {
+ return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
+ ongoingCallController,
+ animationScheduler,
+ locationPublisher,
+ notificationIconAreaController,
+ panelExpansionStateManager,
+ featureFlags,
+ statusBarIconController,
+ statusBarHideIconsForBouncerManager,
+ keyguardStateController,
+ notificationPanelViewController,
+ networkController,
+ statusBarStateController,
+ commandQueue,
+ collapsedStatusBarFragmentLogger,
+ operatorNameViewControllerFactory);
+ }
}
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 9bcf4a5..d6ba6f3 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,8 +53,8 @@
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
@@ -70,12 +70,9 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -94,6 +91,7 @@
private PhoneStatusBarView mStatusBar;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardStateController mKeyguardStateController;
+ private final NotificationPanelViewController mNotificationPanelViewController;
private final NetworkController mNetworkController;
private LinearLayout mSystemIconArea;
private View mClockView;
@@ -102,7 +100,6 @@
private View mCenteredIconArea;
private int mDisabled1;
private int mDisabled2;
- private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private DarkIconManager mDarkIconManager;
private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
private final CommandQueue mCommandQueue;
@@ -143,10 +140,10 @@
NotificationIconAreaController notificationIconAreaController,
PanelExpansionStateManager panelExpansionStateManager,
FeatureFlags featureFlags,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
StatusBarIconController statusBarIconController,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
KeyguardStateController keyguardStateController,
+ NotificationPanelViewController notificationPanelViewController,
NetworkController networkController,
StatusBarStateController statusBarStateController,
CommandQueue commandQueue,
@@ -160,10 +157,10 @@
mNotificationIconAreaController = notificationIconAreaController;
mPanelExpansionStateManager = panelExpansionStateManager;
mFeatureFlags = featureFlags;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
mStatusBarIconController = statusBarIconController;
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mKeyguardStateController = keyguardStateController;
+ mNotificationPanelViewController = notificationPanelViewController;
mNetworkController = networkController;
mStatusBarStateController = statusBarStateController;
mCommandQueue = commandQueue;
@@ -325,8 +322,8 @@
}
protected int adjustDisableFlags(int state) {
- boolean headsUpVisible = mStatusBarOptionalLazy.get()
- .map(StatusBar::headsUpShouldBeVisible).orElse(false);
+ boolean headsUpVisible =
+ mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
if (headsUpVisible) {
state |= DISABLE_CLOCK;
}
@@ -354,8 +351,7 @@
// The shelf will be hidden when dozing with a custom clock, we must show notification
// icons in this occasion.
if (mStatusBarStateController.isDozing()
- && mStatusBarOptionalLazy.get().map(
- sb -> sb.getPanelController().hasCustomClock()).orElse(false)) {
+ && mNotificationPanelViewController.hasCustomClock()) {
state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
}
@@ -396,10 +392,8 @@
}
private boolean shouldHideNotificationIcons() {
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
if (!mPanelExpansionStateManager.isClosed()
- && statusBarOptional.map(
- StatusBar::hideStatusBarIconsWhenExpanded).orElse(false)) {
+ && mNotificationPanelViewController.hideStatusBarIconsWhenExpanded()) {
return true;
}
return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 47c1875..3656ed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -18,7 +18,9 @@
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.BindsInstance;
@@ -56,6 +58,8 @@
// No one accesses this controller, so we need to make sure we reference it here so it does
// get initialized.
getBatteryMeterViewController().init();
+ getHeadsUpAppearanceController().init();
+ getPhoneStatusBarViewController().init();
}
/** */
@@ -66,4 +70,12 @@
@StatusBarFragmentScope
@RootView
PhoneStatusBarView getPhoneStatusBarView();
+
+ /** */
+ @StatusBarFragmentScope
+ PhoneStatusBarViewController getPhoneStatusBarViewController();
+
+ /** */
+ @StatusBarFragmentScope
+ HeadsUpAppearanceController getHeadsUpAppearanceController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 969361b..d244558 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -19,7 +19,9 @@
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.Module;
@@ -43,4 +45,16 @@
static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.battery);
}
+
+ /** */
+ @Provides
+ @StatusBarFragmentScope
+ static PhoneStatusBarViewController providePhoneStatusBarViewController(
+ PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
+ @RootView PhoneStatusBarView phoneStatusBarView,
+ NotificationPanelViewController notificationPanelViewController) {
+ return phoneStatusBarViewControllerFactory.create(
+ phoneStatusBarView,
+ notificationPanelViewController.getStatusBarTouchEventHandler());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index 43b2061..8e4778e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -245,6 +245,10 @@
return mUserSwitcherController.isSimpleUserSwitcher();
}
+ public int getHeight() {
+ return mListView.getHeight();
+ }
+
/**
* @param animate if the transition should be animated
* @return true if the switcher state changed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
index cd8894c..850a4b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
@@ -97,9 +97,12 @@
} else {
// Update clickable state immediately so that the menu feels more responsive
userItemViews[i].setClickable(open);
- // Before running the animation, ensure visibility is set correctly
- userItemViews[i].updateVisibilities(animate || open /* showItem */,
- true /* showTextName */, false /* animate */);
+ // when opening we need to make views visible beforehand so they can be animated
+ if (open) {
+ userItemViews[i].updateVisibilities(true /* showItem */,
+ true /* showTextName */, false /* animate */);
+ }
+
}
}
@@ -117,6 +120,13 @@
setClipChildren(true);
setClipToPadding(true);
mAnimating = false;
+ if (!open) {
+ // after closing we hide children so that height of this view is correct
+ for (int i = 1; i < userItemViews.length; i++) {
+ userItemViews[i].updateVisibilities(false /* showItem */,
+ true /* showTextName */, false /* animate */);
+ }
+ }
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
deleted file mode 100644
index ac8b47d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.policy;
-
-import android.app.StatusBarManager;
-import android.content.Context;
-import android.content.res.Configuration;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import javax.inject.Inject;
-
-/**
- * Let {@link RemoteInputView} to control the visibility of QuickSetting.
- */
-@SysUISingleton
-public class RemoteInputQuickSettingsDisabler
- implements ConfigurationController.ConfigurationListener {
-
- private Context mContext;
- @VisibleForTesting boolean mRemoteInputActive;
- @VisibleForTesting boolean misLandscape;
- private int mLastOrientation;
- private final CommandQueue mCommandQueue;
-
- @Inject
- public RemoteInputQuickSettingsDisabler(Context context,
- ConfigurationController configController, CommandQueue commandQueue) {
- mContext = context;
- mCommandQueue = commandQueue;
- mLastOrientation = mContext.getResources().getConfiguration().orientation;
- configController.addCallback(this);
- }
-
- public int adjustDisableFlags(int state) {
- if (mRemoteInputActive && misLandscape) {
- state |= StatusBarManager.DISABLE2_QUICK_SETTINGS;
- }
-
- return state;
- }
-
- public void setRemoteInputActive(boolean active){
- if(mRemoteInputActive != active){
- mRemoteInputActive = active;
- recomputeDisableFlags();
- }
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- if (newConfig.orientation != mLastOrientation) {
- misLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
- mLastOrientation = newConfig.orientation;
- recomputeDisableFlags();
- }
- }
-
- /**
- * Reapplies the disable flags. Then the method adjustDisableFlags in this class will be invoked
- * in {@link QSFragment#disable(int, int, boolean)} and
- * {@link StatusBar#disable(int, int, boolean)}
- * to modify the disable flags according to the status of mRemoteInputActive and misLandscape.
- */
- private void recomputeDisableFlags() {
- mCommandQueue.recomputeDisableFlags(mContext.getDisplayId(), true);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
new file mode 100644
index 0000000..31ef2f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.policy
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.content.res.Configuration
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+
+/**
+ * Controls whether the disable flag [StatusBarManager.DISABLE2_QUICK_SETTINGS] should be set.
+ * This would happen when a [RemoteInputView] is active, the device is in landscape and not using
+ * split shade.
+ */
+@SysUISingleton
+class RemoteInputQuickSettingsDisabler @Inject constructor(
+ private val context: Context,
+ private val commandQueue: CommandQueue,
+ configController: ConfigurationController
+) : ConfigurationController.ConfigurationListener {
+
+ private var remoteInputActive = false
+ private var isLandscape: Boolean
+ private var shouldUseSplitNotificationShade: Boolean
+
+ init {
+ isLandscape =
+ context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
+ shouldUseSplitNotificationShade = Utils.shouldUseSplitNotificationShade(context.resources)
+ configController.addCallback(this)
+ }
+
+ fun adjustDisableFlags(state: Int): Int {
+ var mutableState = state
+ if (remoteInputActive &&
+ isLandscape &&
+ !shouldUseSplitNotificationShade
+ ) {
+ mutableState = state or StatusBarManager.DISABLE2_QUICK_SETTINGS
+ }
+ return mutableState
+ }
+
+ fun setRemoteInputActive(active: Boolean) {
+ if (remoteInputActive != active) {
+ remoteInputActive = active
+ recomputeDisableFlags()
+ }
+ }
+
+ override fun onConfigChanged(newConfig: Configuration) {
+ var needToRecompute = false
+
+ val newIsLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
+ if (newIsLandscape != isLandscape) {
+ isLandscape = newIsLandscape
+ needToRecompute = true
+ }
+
+ val newSplitShadeFlag = Utils.shouldUseSplitNotificationShade(context.resources)
+ if (newSplitShadeFlag != shouldUseSplitNotificationShade) {
+ shouldUseSplitNotificationShade = newSplitShadeFlag
+ needToRecompute = true
+ }
+ if (needToRecompute) {
+ recomputeDisableFlags()
+ }
+ }
+
+ /**
+ * Called in order to trigger a refresh of the disable flags after a relevant configuration
+ * change or when a [RemoteInputView] has changed its active state. The method
+ * [adjustDisableFlags] will be invoked to modify the disable flags according to
+ * [remoteInputActive], [isLandscape] and [shouldUseSplitNotificationShade].
+ */
+ private fun recomputeDisableFlags() {
+ commandQueue.recomputeDisableFlags(context.displayId, true)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 85add6c..aec9db5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -219,6 +219,7 @@
private void clearLayoutLineCount(View view) {
if (view instanceof TextView) {
((TextView) view).nullLayouts();
+ view.forceLayout();
}
}
@@ -270,6 +271,9 @@
clearLayoutLineCount(child);
child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
+ if (((Button) child).getLayout() == null) {
+ Log.wtf(TAG, "Button layout is null after measure.");
+ }
coveredSuggestions.add(child);
@@ -590,6 +594,9 @@
button.getPaddingLeft() + button.getPaddingRight() + textWidth
+ getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
button.measure(widthMeasureSpec, heightMeasureSpec);
+ if (button.getLayout() == null) {
+ Log.wtf(TAG, "Button layout is null after measure.");
+ }
final int newWidth = button.getMeasuredWidth();
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 fd387ae..ebf5a6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -69,6 +69,7 @@
import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
import com.android.systemui.SystemUISecondaryUserService;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -133,6 +134,7 @@
private final IActivityTaskManager mActivityTaskManager;
private final InteractionJankMonitor mInteractionJankMonitor;
private final LatencyTracker mLatencyTracker;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
@VisibleForTesting
@@ -180,7 +182,8 @@
@Background Executor bgExecutor,
InteractionJankMonitor interactionJankMonitor,
LatencyTracker latencyTracker,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mActivityManager = activityManager;
mUserTracker = userTracker;
@@ -208,6 +211,8 @@
mHandler = handler;
mActivityStarter = activityStarter;
mUserManager = userManager;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -1156,7 +1161,7 @@
? com.android.settingslib.R.string.guest_reset_guest_dialog_title
: R.string.guest_exit_guest_dialog_title);
setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
- setButton(DialogInterface.BUTTON_NEGATIVE,
+ setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(mGuestUserAutoCreated
@@ -1175,11 +1180,11 @@
if (mFalsingManager.isFalseTap(penalty)) {
return;
}
- if (which == BUTTON_NEGATIVE) {
+ if (which == BUTTON_NEUTRAL) {
cancel();
} else {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
- dismissStack();
+ mDialogLaunchAnimator.dismissStack(this);
removeGuestUser(mGuestId, mTargetId);
}
}
@@ -1193,7 +1198,7 @@
super(context);
setTitle(R.string.user_add_user_title);
setMessage(context.getString(R.string.user_add_user_message_short));
- setButton(DialogInterface.BUTTON_NEGATIVE,
+ setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(android.R.string.ok), this);
@@ -1207,10 +1212,10 @@
if (mFalsingManager.isFalseTap(penalty)) {
return;
}
- if (which == BUTTON_NEGATIVE) {
+ if (which == BUTTON_NEUTRAL) {
cancel();
} else {
- dismissStack();
+ mDialogLaunchAnimator.dismissStack(this);
if (ActivityManager.isUserAMonkey()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 592fa15..70cb9d3 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -48,6 +48,8 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TypedValue;
import androidx.annotation.NonNull;
@@ -104,14 +106,15 @@
private final UserManager mUserManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Executor mBgExecutor;
- private SecureSettings mSecureSettings;
+ private final SecureSettings mSecureSettings;
private final Executor mMainExecutor;
private final Handler mBgHandler;
private final boolean mIsMonetEnabled;
- private UserTracker mUserTracker;
- private DeviceProvisionedController mDeviceProvisionedController;
- private WallpaperColors mCurrentColors;
- private WallpaperManager mWallpaperManager;
+ private final UserTracker mUserTracker;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+ // Current wallpaper colors associated to a user.
+ private final SparseArray<WallpaperColors> mCurrentColors = new SparseArray<>();
+ private final WallpaperManager mWallpaperManager;
private ColorScheme mColorScheme;
// If fabricated overlays were already created for the current theme.
private boolean mNeedsOverlayCreation;
@@ -125,11 +128,11 @@
private FabricatedOverlay mNeutralOverlay;
// If wallpaper color event will be accepted and change the UI colors.
private boolean mAcceptColorEvents = true;
- // If non-null, colors that were sent to the framework, and processing was deferred until
- // the next time the screen is off.
- private WallpaperColors mDeferredWallpaperColors;
- private int mDeferredWallpaperColorsFlags;
- private WakefulnessLifecycle mWakefulnessLifecycle;
+ // If non-null (per user), colors that were sent to the framework, and processing was deferred
+ // until the next time the screen is off.
+ private final SparseArray<WallpaperColors> mDeferredWallpaperColors = new SparseArray<>();
+ private final SparseIntArray mDeferredWallpaperColorsFlags = new SparseIntArray();
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
// Defers changing themes until Setup Wizard is done.
private boolean mDeferredThemeEvaluation;
@@ -152,27 +155,53 @@
}
};
- private final OnColorsChangedListener mOnColorsChangedListener = (wallpaperColors, which) -> {
- if (!mAcceptColorEvents && mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP) {
- mDeferredWallpaperColors = wallpaperColors;
- mDeferredWallpaperColorsFlags = which;
- Log.i(TAG, "colors received; processing deferred until screen off: " + wallpaperColors);
- return;
+ private final OnColorsChangedListener mOnColorsChangedListener = new OnColorsChangedListener() {
+ @Override
+ public void onColorsChanged(WallpaperColors wallpaperColors, int which) {
+ throw new IllegalStateException("This should never be invoked, all messages should "
+ + "arrive on the overload that has a user id");
}
- if (wallpaperColors != null) {
- mAcceptColorEvents = false;
- // Any cache of colors deferred for process is now stale.
- mDeferredWallpaperColors = null;
- mDeferredWallpaperColorsFlags = 0;
- }
+ @Override
+ public void onColorsChanged(WallpaperColors wallpaperColors, int which, int userId) {
+ boolean currentUser = userId == mUserTracker.getUserId();
+ if (currentUser && !mAcceptColorEvents
+ && mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP) {
+ mDeferredWallpaperColors.put(userId, wallpaperColors);
+ mDeferredWallpaperColorsFlags.put(userId, which);
+ Log.i(TAG, "colors received; processing deferred until screen off: "
+ + wallpaperColors + " user: " + userId);
+ return;
+ }
- handleWallpaperColors(wallpaperColors, which);
+ if (currentUser && wallpaperColors != null) {
+ mAcceptColorEvents = false;
+ // Any cache of colors deferred for process is now stale.
+ mDeferredWallpaperColors.put(userId, null);
+ mDeferredWallpaperColorsFlags.put(userId, 0);
+ }
+
+ handleWallpaperColors(wallpaperColors, which, userId);
+ }
};
- private int getLatestWallpaperType() {
- return mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)
- > mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM)
+ private final UserTracker.Callback mUserTrackerCallback = new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ boolean isManagedProfile = mUserManager.isManagedProfile(newUser);
+ if (!mDeviceProvisionedController.isCurrentUserSetup() && isManagedProfile) {
+ Log.i(TAG, "User setup not finished when new user event was received. "
+ + "Deferring... Managed profile? " + isManagedProfile);
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
+ reevaluateSystemTheme(true /* forceReload */);
+ }
+ };
+
+ private int getLatestWallpaperType(int userId) {
+ return mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, userId)
+ > mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, userId)
? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM;
}
@@ -204,14 +233,21 @@
return false;
}
- private void handleWallpaperColors(WallpaperColors wallpaperColors, int flags) {
- final boolean hadWallpaperColors = mCurrentColors != null;
- int latestWallpaperType = getLatestWallpaperType();
+ private void handleWallpaperColors(WallpaperColors wallpaperColors, int flags, int userId) {
+ final int currentUser = mUserTracker.getUserId();
+ final boolean hadWallpaperColors = mCurrentColors.get(userId) != null;
+ int latestWallpaperType = getLatestWallpaperType(userId);
if ((flags & latestWallpaperType) != 0) {
- mCurrentColors = wallpaperColors;
+ mCurrentColors.put(userId, wallpaperColors);
if (DEBUG) Log.d(TAG, "got new colors: " + wallpaperColors + " where: " + flags);
}
+ if (userId != currentUser) {
+ Log.d(TAG, "Colors " + wallpaperColors + " for user " + userId + ". "
+ + "Not for current user: " + currentUser);
+ return;
+ }
+
if (mDeviceProvisionedController != null
&& !mDeviceProvisionedController.isCurrentUserSetup()) {
if (hadWallpaperColors) {
@@ -226,13 +262,12 @@
} else {
if (DEBUG) {
Log.i(TAG, "During user setup, but allowing first color event: had? "
- + hadWallpaperColors + " has? " + (mCurrentColors != null));
+ + hadWallpaperColors + " has? " + (mCurrentColors.get(userId) != null));
}
}
}
// Check if we need to reset to default colors (if a color override was set that is sourced
// from the wallpaper)
- int currentUser = mUserTracker.getUserId();
String overlayPackageJson = mSecureSettings.getStringForUser(
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
currentUser);
@@ -278,10 +313,9 @@
@Override
public void onReceive(Context context, Intent intent) {
boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction());
- boolean userStarted = Intent.ACTION_USER_SWITCHED.equals(intent.getAction());
boolean isManagedProfile = mUserManager.isManagedProfile(
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
- if (userStarted || newWorkProfile) {
+ if (newWorkProfile) {
if (!mDeviceProvisionedController.isCurrentUserSetup() && isManagedProfile) {
Log.i(TAG, "User setup not finished when " + intent.getAction()
+ " was received. Deferring... Managed profile? " + isManagedProfile);
@@ -330,7 +364,6 @@
public void start() {
if (DEBUG) Log.d(TAG, "Start");
final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
@@ -365,15 +398,17 @@
return;
}
+ mUserTracker.addCallback(mUserTrackerCallback, mMainExecutor);
+
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
// Upon boot, make sure we have the most up to date colors
Runnable updateColors = () -> {
WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
- getLatestWallpaperType());
+ getLatestWallpaperType(mUserTracker.getUserId()));
Runnable applyColors = () -> {
if (DEBUG) Log.d(TAG, "Boot colors: " + systemColor);
- mCurrentColors = systemColor;
+ mCurrentColors.put(mUserTracker.getUserId(), systemColor);
reevaluateSystemTheme(false /* forceReload */);
};
if (mDeviceProvisionedController.isCurrentUserSetup()) {
@@ -395,21 +430,22 @@
mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedGoingToSleep() {
- if (mDeferredWallpaperColors != null) {
- WallpaperColors colors = mDeferredWallpaperColors;
- int flags = mDeferredWallpaperColorsFlags;
+ final int userId = mUserTracker.getUserId();
+ final WallpaperColors colors = mDeferredWallpaperColors.get(userId);
+ if (colors != null) {
+ int flags = mDeferredWallpaperColorsFlags.get(userId);
- mDeferredWallpaperColors = null;
- mDeferredWallpaperColorsFlags = 0;
+ mDeferredWallpaperColors.put(userId, null);
+ mDeferredWallpaperColorsFlags.put(userId, 0);
- handleWallpaperColors(colors, flags);
+ handleWallpaperColors(colors, flags, userId);
}
}
});
}
private void reevaluateSystemTheme(boolean forceReload) {
- final WallpaperColors currentColors = mCurrentColors;
+ final WallpaperColors currentColors = mCurrentColors.get(mUserTracker.getUserId());
final int mainColor;
final int accentCandidate;
if (currentColors == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index 8b394bf..a3b2932 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -200,7 +200,9 @@
iconView.setVisibility(View.GONE);
} else {
iconView.setImageDrawable(icon);
- if (appInfo.labelRes != 0) {
+ if (appInfo == null) {
+ Log.d(TAG, "No appInfo for pkg=" + mPackageName + " usr=" + mUserId);
+ } else if (appInfo.labelRes != 0) {
try {
Resources res = mContext.getPackageManager().getResourcesForApplication(
appInfo,
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 51de1321..e6fc49f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.unfold
+import android.animation.ValueAnimator
import android.content.Context
import android.graphics.PixelFormat
import android.hardware.devicestate.DeviceStateManager
@@ -111,7 +112,7 @@
Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn")
try {
// Add the view only if we are unfolding and this is the first screen on
- if (!isFolded && !isUnfoldHandled) {
+ if (!isFolded && !isUnfoldHandled && ValueAnimator.areAnimatorsEnabled()) {
addView(onOverlayReady)
isUnfoldHandled = true
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index c3f9c9f..1fd47a4 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -56,6 +56,8 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.ShellCommandHandler;
+import com.android.wm.shell.compatui.CompatUI;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.nano.WmShellTraceProto;
@@ -65,7 +67,6 @@
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.sizecompatui.SizeCompatUI;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
@@ -113,7 +114,8 @@
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final Optional<ShellCommandHandler> mShellCommandHandler;
- private final Optional<SizeCompatUI> mSizeCompatUIOptional;
+ private final Optional<CompatUI> mCompatUIOptional;
+ private final Optional<DragAndDrop> mDragAndDropOptional;
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
@@ -130,7 +132,7 @@
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
- private KeyguardUpdateMonitorCallback mSizeCompatUIKeyguardCallback;
+ private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
@Inject
@@ -141,7 +143,8 @@
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
Optional<ShellCommandHandler> shellCommandHandler,
- Optional<SizeCompatUI> sizeCompatUIOptional,
+ Optional<CompatUI> sizeCompatUIOptional,
+ Optional<DragAndDrop> dragAndDropOptional,
CommandQueue commandQueue,
ConfigurationController configurationController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -166,7 +169,8 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mProtoTracer = protoTracer;
mShellCommandHandler = shellCommandHandler;
- mSizeCompatUIOptional = sizeCompatUIOptional;
+ mCompatUIOptional = sizeCompatUIOptional;
+ mDragAndDropOptional = dragAndDropOptional;
mSysUiMainExecutor = sysUiMainExecutor;
}
@@ -181,7 +185,8 @@
mSplitScreenOptional.ifPresent(this::initSplitScreen);
mOneHandedOptional.ifPresent(this::initOneHanded);
mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
- mSizeCompatUIOptional.ifPresent(this::initSizeCompatUi);
+ mCompatUIOptional.ifPresent(this::initCompatUi);
+ mDragAndDropOptional.ifPresent(this::initDragAndDrop);
}
@VisibleForTesting
@@ -260,6 +265,18 @@
}
};
mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
+
+ mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onFinishedWakingUp() {
+ splitScreen.onFinishedWakingUp();
+ }
+
+ @Override
+ public void onFinishedGoingToSleep() {
+ splitScreen.onFinishedGoingToSleep();
+ }
+ });
}
@VisibleForTesting
@@ -374,14 +391,28 @@
}
@VisibleForTesting
- void initSizeCompatUi(SizeCompatUI sizeCompatUI) {
- mSizeCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ void initCompatUi(CompatUI sizeCompatUI) {
+ mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onKeyguardOccludedChanged(boolean occluded) {
sizeCompatUI.onKeyguardOccludedChanged(occluded);
}
};
- mKeyguardUpdateMonitor.registerCallback(mSizeCompatUIKeyguardCallback);
+ mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback);
+ }
+
+ void initDragAndDrop(DragAndDrop dragAndDrop) {
+ mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ dragAndDrop.onConfigChanged(newConfig);
+ }
+
+ @Override
+ public void onThemeChanged() {
+ dragAndDrop.onThemeChanged();
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index de8cc89..70792cf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -669,7 +669,7 @@
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
- verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
+ verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
@@ -1056,6 +1056,16 @@
verify(callback, atLeastOnce()).onRequireUnlockForNfc();
}
+ @Test
+ public void testFaceDoesNotAuth_afterPinAttempt() {
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ verify(mFingerprintManager, never()).authenticate(any(), any(), any(),
+ any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+ anyBoolean());
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 6237031..f56bf69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -282,9 +282,12 @@
mTargetsObserver = spy(Dependency.get(AccessibilityButtonTargetsObserver.class));
mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ final AccessibilityFloatingMenuController controller =
+ new AccessibilityFloatingMenuController(mContextWrapper, mTargetsObserver,
+ mModeObserver, mKeyguardUpdateMonitor);
+ controller.init();
- return new AccessibilityFloatingMenuController(mContextWrapper, mTargetsObserver,
- mModeObserver, mKeyguardUpdateMonitor);
+ return controller;
}
private void enableAccessibilityFloatingMenuConfig() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index d819fa2..1fe3d44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -46,7 +46,7 @@
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
- private val launchAnimator = LaunchAnimator(mContext, isForTesting = true)
+ private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
@Mock lateinit var callback: ActivityLaunchAnimator.Callback
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 9bd33eb..b951345 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -2,35 +2,50 @@
import android.app.Dialog
import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
import android.os.Bundle
+import android.service.dreams.IDreamManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
import android.view.View
import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
+import com.android.internal.policy.DecorView
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogListener.DismissReason
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertTrue
import org.junit.After
+import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class DialogLaunchAnimatorTest : SysuiTestCase() {
- private val launchAnimator = LaunchAnimator(context, isForTesting = true)
- private val hostDialogprovider = TestHostDialogProvider()
- private val dialogLaunchAnimator =
- DialogLaunchAnimator(context, launchAnimator, hostDialogprovider)
-
+ private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+ private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private val attachedViews = mutableSetOf<View>()
+ @Mock lateinit var dreamManager: IDreamManager
+ @get:Rule val rule = MockitoJUnit.rule()
+
+ @Before
+ fun setUp() {
+ dialogLaunchAnimator = DialogLaunchAnimator(
+ dreamManager, launchAnimator, isForTesting = true)
+ }
+
@After
fun tearDown() {
runOnMainThreadAndWaitForIdleSync {
@@ -44,76 +59,66 @@
fun testShowDialogFromView() {
// Show the dialog. showFromView() must be called on the main thread with a dialog created
// on the main thread too.
- val (dialog, hostDialog) = createDialogAndHostDialog()
+ val dialog = createAndShowDialog()
- // Only the host dialog is actually showing.
- assertTrue(hostDialog.isShowing)
- assertFalse(dialog.isShowing)
+ assertTrue(dialog.isShowing)
- // The dialog onStart() method was called but not onStop().
- assertTrue(dialog.onStartCalled)
- assertFalse(dialog.onStopCalled)
+ // The dialog is now fullscreen.
+ val window = dialog.window
+ val decorView = window.decorView as DecorView
+ assertEquals(MATCH_PARENT, window.attributes.width)
+ assertEquals(MATCH_PARENT, window.attributes.height)
+ assertEquals(MATCH_PARENT, decorView.layoutParams.width)
+ assertEquals(MATCH_PARENT, decorView.layoutParams.height)
- // The dialog content has been stolen and is shown inside the host dialog.
- val hostDialogContent = hostDialog.findViewById<ViewGroup>(android.R.id.content)
- assertEquals(0, dialog.findViewById<ViewGroup>(android.R.id.content).childCount)
- assertEquals(1, hostDialogContent.childCount)
+ // The single DecorView child is a transparent fullscreen view that will dismiss the dialog
+ // when clicked.
+ assertEquals(1, decorView.childCount)
+ val transparentBackground = decorView.getChildAt(0) as ViewGroup
+ assertEquals(MATCH_PARENT, transparentBackground.layoutParams.width)
+ assertEquals(MATCH_PARENT, transparentBackground.layoutParams.height)
- // The original dialog content is added to another view that is the same size as the
- // original dialog window.
- val hostDialogRoot = hostDialogContent.getChildAt(0) as ViewGroup
- assertEquals(1, hostDialogRoot.childCount)
+ // The single transparent background child is a fake window with the same size and
+ // background as the dialog initially had.
+ assertEquals(1, transparentBackground.childCount)
+ val dialogContentWithBackground = transparentBackground.getChildAt(0) as ViewGroup
+ assertEquals(TestDialog.DIALOG_WIDTH, dialogContentWithBackground.layoutParams.width)
+ assertEquals(TestDialog.DIALOG_HEIGHT, dialogContentWithBackground.layoutParams.height)
+ assertEquals(dialog.windowBackground, dialogContentWithBackground.background)
- val dialogContentParent = hostDialogRoot.getChildAt(0) as ViewGroup
- assertEquals(1, dialogContentParent.childCount)
- assertEquals(TestDialog.DIALOG_WIDTH, dialogContentParent.layoutParams.width)
- assertEquals(TestDialog.DIALOG_HEIGHT, dialogContentParent.layoutParams.height)
+ // The dialog content is inside this fake window view.
+ assertNotNull(
+ dialogContentWithBackground.findViewByPredicate { it === dialog.contentView })
- val dialogContent = dialogContentParent.getChildAt(0)
- assertEquals(dialog.contentView, dialogContent)
- assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, dialogContent.layoutParams.width)
- assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, dialogContent.layoutParams.height)
-
- // Hiding/showing/dismissing the dialog should hide/show/dismiss the host dialog given that
- // it's a ListenableDialog.
- runOnMainThreadAndWaitForIdleSync { dialog.hide() }
- assertFalse(hostDialog.isShowing)
- assertFalse(dialog.isShowing)
-
- runOnMainThreadAndWaitForIdleSync { dialog.show() }
- assertTrue(hostDialog.isShowing)
- assertFalse(dialog.isShowing)
-
- assertFalse(dialog.onStopCalled)
+ // Clicking the transparent background should dismiss the dialog.
runOnMainThreadAndWaitForIdleSync {
// TODO(b/204561691): Remove this call to disableAllCurrentDialogsExitAnimations() and
// make sure that the test still pass on git_master/cf_x86_64_phone-userdebug in
// Forrest.
dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
- dialog.dismiss()
+ transparentBackground.performClick()
}
- assertFalse(hostDialog.isShowing)
assertFalse(dialog.isShowing)
- assertTrue(hostDialog.wasDismissed)
- assertTrue(dialog.onStopCalled)
}
@Test
fun testStackedDialogsDismissesAll() {
- val (_, hostDialogFirst) = createDialogAndHostDialog()
- val (dialogSecond, hostDialogSecond) = createDialogAndHostDialogFromDialog(hostDialogFirst)
+ val firstDialog = createAndShowDialog()
+ val secondDialog = createDialogAndShowFromDialog(firstDialog)
+ assertTrue(firstDialog.isShowing)
+ assertTrue(secondDialog.isShowing)
runOnMainThreadAndWaitForIdleSync {
dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
- dialogSecond.dismissStack()
+ dialogLaunchAnimator.dismissStack(secondDialog)
}
- assertTrue(hostDialogSecond.wasDismissed)
- assertTrue(hostDialogFirst.wasDismissed)
+ assertFalse(firstDialog.isShowing)
+ assertFalse(secondDialog.isShowing)
}
- private fun createDialogAndHostDialog(): Pair<TestDialog, TestHostDialog> {
+ private fun createAndShowDialog(): TestDialog {
return runOnMainThreadAndWaitForIdleSync {
val touchSurfaceRoot = LinearLayout(context)
val touchSurface = View(context)
@@ -125,22 +130,16 @@
attachedViews.add(touchSurfaceRoot)
val dialog = TestDialog(context)
- val hostDialog =
- dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog
- dialog to hostDialog
+ dialogLaunchAnimator.showFromView(dialog, touchSurface)
+ dialog
}
}
- private fun createDialogAndHostDialogFromDialog(
- hostParent: Dialog
- ): Pair<TestDialog, TestHostDialog> {
+ private fun createDialogAndShowFromDialog(animateFrom: Dialog): TestDialog {
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
- val hostDialog = dialogLaunchAnimator.showFromDialog(
- dialog,
- hostParent
- ) as TestHostDialog
- dialog to hostDialog
+ dialogLaunchAnimator.showFromDialog(dialog, animateFrom)
+ dialog
}
}
@@ -153,50 +152,14 @@
return result
}
- private class TestHostDialogProvider : HostDialogProvider {
- override fun createHostDialog(
- context: Context,
- theme: Int,
- onCreateCallback: () -> Unit,
- dismissOverride: (() -> Unit) -> Unit
- ): Dialog = TestHostDialog(context, onCreateCallback, dismissOverride)
- }
-
- private class TestHostDialog(
- context: Context,
- private val onCreateCallback: () -> Unit,
- private val dismissOverride: (() -> Unit) -> Unit
- ) : Dialog(context) {
- var wasDismissed = false
-
- init {
- // We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
- window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- onCreateCallback()
- }
-
- override fun dismiss() {
- dismissOverride {
- super.dismiss()
- wasDismissed = true
- }
- }
- }
-
- private class TestDialog(context: Context) : Dialog(context), ListenableDialog {
+ private class TestDialog(context: Context) : Dialog(context) {
companion object {
const val DIALOG_WIDTH = 100
const val DIALOG_HEIGHT = 200
}
- private val listeners = hashSetOf<DialogListener>()
val contentView = View(context)
- var onStartCalled = false
- var onStopCalled = false
+ val windowBackground = ColorDrawable(Color.RED)
init {
// We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
@@ -205,52 +168,10 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- window.setLayout(DIALOG_WIDTH, DIALOG_HEIGHT)
setContentView(contentView)
- }
- override fun onStart() {
- super.onStart()
- onStartCalled = true
- }
-
- override fun onStop() {
- super.onStart()
- onStopCalled = true
- }
-
- override fun addListener(listener: DialogListener) {
- listeners.add(listener)
- }
-
- override fun removeListener(listener: DialogListener) {
- listeners.remove(listener)
- }
-
- override fun dismiss() {
- super.dismiss()
- notifyListeners { onDismiss(DismissReason.UNKNOWN) }
- }
-
- override fun hide() {
- super.hide()
- notifyListeners { onHide() }
- }
-
- override fun show() {
- super.show()
- notifyListeners { onShow() }
- }
-
- fun dismissStack() {
- notifyListeners { prepareForStackDismiss() }
- dismiss()
- }
-
- private fun notifyListeners(notify: DialogListener.() -> Unit) {
- for (listener in HashSet(listeners)) {
- listener.notify()
- }
+ window.setLayout(DIALOG_WIDTH, DIALOG_HEIGHT)
+ window.setBackgroundDrawable(windowBackground)
}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
new file mode 100644
index 0000000..dadf94e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
@@ -0,0 +1,23 @@
+package com.android.systemui.animation
+
+/**
+ * A [LaunchAnimator.Timings] to be used in tests.
+ *
+ * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
+ * when computing the progress of a sub-animation (the contents fade in/out).
+ */
+val TEST_TIMINGS = LaunchAnimator.Timings(
+ totalDuration = 0L,
+ contentBeforeFadeOutDelay = 1L,
+ contentBeforeFadeOutDuration = 1L,
+ contentAfterFadeInDelay = 1L,
+ contentAfterFadeInDuration = 1L
+)
+
+/** A [LaunchAnimator.Interpolators] to be used in tests. */
+val TEST_INTERPOLATORS = LaunchAnimator.Interpolators(
+ positionInterpolator = Interpolators.STANDARD,
+ positionXInterpolator = Interpolators.STANDARD,
+ contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
+ contentAfterFadeInInterpolator = Interpolators.STANDARD
+)
\ No newline at end of file
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 8dd5d6c..08c7714 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,12 +54,14 @@
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.WindowManager;
@@ -67,6 +70,8 @@
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.Execution;
+import com.android.systemui.util.concurrency.FakeExecution;
import org.junit.Before;
import org.junit.Test;
@@ -112,20 +117,27 @@
private SidefpsController mSidefpsController;
@Mock
private DisplayManager mDisplayManager;
- @Mock
- private Handler mHandler;
@Captor
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
+ @Captor
+ ArgumentCaptor<FingerprintStateListener> mFingerprintStateCaptor;
+ private TestableContext mContextSpy;
+ private Execution mExecution;
+ private TestableLooper mTestableLooper;
+ private Handler mHandler;
private TestableAuthController mAuthController;
@Before
public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
- TestableContext context = spy(mContext);
+ mContextSpy = spy(mContext);
+ mExecution = new FakeExecution();
+ mTestableLooper = TestableLooper.get(this);
+ mHandler = new Handler(mTestableLooper.getLooper());
- when(context.getPackageManager()).thenReturn(mPackageManager);
+ when(mContextSpy.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
@@ -158,21 +170,78 @@
props.add(prop);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
- mAuthController = new TestableAuthController(context, mCommandQueue,
+ mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
() -> mUdfpsController, () -> mSidefpsController);
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
mAuthenticatorsRegisteredCaptor.capture());
+
mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
+
+ // Ensures that the operations posted on the handler get executed.
+ mTestableLooper.processAllMessages();
}
// Callback tests
@Test
+ public void testRegistersFingerprintStateListener_afterAllAuthenticatorsAreRegistered()
+ throws RemoteException {
+ // This test is sensitive to prior FingerprintManager interactions.
+ reset(mFingerprintManager);
+
+ // This test requires an uninitialized AuthController.
+ AuthController authController = new TestableAuthController(mContextSpy, mExecution,
+ mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
+ mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
+ authController.start();
+
+ verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
+ mAuthenticatorsRegisteredCaptor.capture());
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager, never()).registerFingerprintStateListener(any());
+
+ mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager).registerFingerprintStateListener(any());
+ }
+
+ @Test
+ public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException {
+ // This test is sensitive to prior FingerprintManager interactions.
+ reset(mFingerprintManager);
+
+ // This test requires an uninitialized AuthController.
+ AuthController authController = new TestableAuthController(mContextSpy, mExecution,
+ mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
+ mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
+ authController.start();
+
+ verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
+ mAuthenticatorsRegisteredCaptor.capture());
+
+ // Emulates a device with no authenticators (empty list).
+ mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager).registerFingerprintStateListener(
+ mFingerprintStateCaptor.capture());
+
+ // Enrollments changed for an unknown sensor.
+ mFingerprintStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */,
+ 0xbeef /* sensorId */, true /* hasEnrollments */);
+ mTestableLooper.processAllMessages();
+
+ // Nothing should crash.
+ }
+
+ @Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
- showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
+ showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
@@ -497,7 +566,7 @@
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
mAuthController.mTaskStackListener.onTaskStackChanged();
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
@@ -528,7 +597,7 @@
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mAuthController.mBroadcastReceiver.onReceive(mContext, intent);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
@@ -598,6 +667,7 @@
private PromptInfo mLastBiometricPromptInfo;
TestableAuthController(Context context,
+ Execution execution,
CommandQueue commandQueue,
ActivityTaskManager activityTaskManager,
WindowManager windowManager,
@@ -605,7 +675,7 @@
FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
Provider<SidefpsController> sidefpsControllerFactory) {
- super(context, commandQueue, activityTaskManager, windowManager,
+ super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory,
sidefpsControllerFactory, mDisplayManager, mHandler);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 2c4808a..5128ccc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -131,7 +131,7 @@
false /* isStrongBiometric */)
// THEN update sensor location and show ripple
- verify(rippleView).setSensorLocation(fpsLocation)
+ verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f)
verify(rippleView).startUnlockedRipple(any())
}
@@ -292,10 +292,10 @@
reset(rippleView)
captor.value.onThemeChanged()
- verify(rippleView).setColor(ArgumentMatchers.anyInt())
+ verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
reset(rippleView)
captor.value.onUiModeChanged()
- verify(rippleView).setColor(ArgumentMatchers.anyInt())
+ verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 9e42ff3..ac221a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -65,6 +65,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -170,6 +171,8 @@
private TypedArray mBrightnessValues;
@Mock
private TypedArray mBrightnessBacklight;
+ @Mock
+ private SystemUIDialogManager mSystemUIDialogManager;
// Capture listeners so that they can be used to send events
@Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -179,8 +182,6 @@
@Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
private ScreenLifecycle.Observer mScreenObserver;
- @Captor private ArgumentCaptor<UdfpsAnimationViewController> mAnimViewControllerCaptor;
-
@Before
public void setUp() {
setUpResources();
@@ -238,7 +239,8 @@
mHandler,
mConfigurationController,
mSystemClock,
- mUnlockedScreenOffAnimationController);
+ mUnlockedScreenOffAnimationController,
+ mSystemUIDialogManager);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -642,12 +644,12 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
moveEvent.recycle();
- // THEN low-tick haptic is played
+ // THEN click haptic is played
verify(mVibrator).vibrate(
anyInt(),
anyString(),
any(),
- eq("udfps-onStart-tick"),
+ eq("udfps-onStart-click"),
eq(UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES));
// THEN make sure vibration attributes has so that it always will play the haptic,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 0e86964..0ae3c39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -92,6 +93,8 @@
@Mock
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock
+ private SystemUIDialogManager mDialogManager;
+ @Mock
private UdfpsController mUdfpsController;
private FakeSystemClock mSystemClock = new FakeSystemClock();
@@ -130,6 +133,7 @@
mSystemClock,
mKeyguardStateController,
mUnlockedScreenOffAnimationController,
+ mDialogManager,
mUdfpsController);
}
@@ -351,9 +355,8 @@
mSystemClock.advanceTime(205);
mController.onTouchOutsideView();
- // THEN show the bouncer and reset alt auth
+ // THEN show the bouncer
verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
- verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
new file mode 100644
index 0000000..87b9172
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.systemui.controls.ui
+
+import android.app.PendingIntent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.TaskView
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class DetailDialogTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var taskView: TaskView
+ @Mock
+ private lateinit var controlViewHolder: ControlViewHolder
+ @Mock
+ private lateinit var pendingIntent: PendingIntent
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testPendingIntentIsUnModified() {
+ // GIVEN the dialog is created with a PendingIntent
+ val dialog = createDialog(pendingIntent)
+
+ // WHEN the TaskView is initialized
+ dialog.stateCallback.onInitialized()
+
+ // THEN the PendingIntent used to call startActivity is unmodified by systemui
+ verify(taskView).startActivity(eq(pendingIntent), any(), any(), any())
+ }
+
+ private fun createDialog(pendingIntent: PendingIntent): DetailDialog {
+ return DetailDialog(
+ mContext,
+ taskView,
+ pendingIntent,
+ controlViewHolder
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 1d34aac..519d7d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -27,8 +27,6 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -91,8 +89,7 @@
mHandler = mHandlerThread.getThreadHandler();
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- () -> mStatusBarStateController, mConfigurationController);
+ mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, () -> mStatusBarStateController);
mDozeUi.setDozeMachine(mMachine);
}
@@ -104,7 +101,7 @@
}
@Test
- public void pausingAndUnpausingAod_registersTimeTickAfterUnpausing() throws Exception {
+ public void pausingAndUnpausingAod_registersTimeTickAfterUnpausing() {
mDozeUi.transitionTo(UNINITIALIZED, INITIALIZED);
mDozeUi.transitionTo(INITIALIZED, DOZE_AOD);
mDozeUi.transitionTo(DOZE_AOD, DOZE_AOD_PAUSED);
@@ -117,45 +114,9 @@
}
@Test
- public void propagatesAnimateScreenOff_noAlwaysOn() {
- reset(mHost);
- when(mDozeParameters.getAlwaysOn()).thenReturn(false);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
-
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
- verify(mHost).setAnimateScreenOff(eq(false));
- }
-
- @Test
- public void propagatesAnimateScreenOff_alwaysOn() {
- reset(mHost);
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
-
- // Take over when the keyguard is visible.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
- verify(mHost).setAnimateScreenOff(eq(true));
-
- // Do not animate screen-off when keyguard isn't visible - PowerManager will do it.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
- verify(mHost).setAnimateScreenOff(eq(false));
- }
-
- @Test
- public void neverAnimateScreenOff_whenNotSupported() {
- // Re-initialize DozeParameters saying that the display requires blanking.
- reset(mDozeParameters);
- reset(mHost);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
- mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- () -> mStatusBarStateController, mConfigurationController);
- mDozeUi.setDozeMachine(mMachine);
-
- // Never animate if display doesn't support it.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
- verify(mHost, never()).setAnimateScreenOff(eq(false));
+ public void transitionSetsAnimateWakeup_noAlwaysOn() {
+ mDozeUi.transitionTo(UNINITIALIZED, DOZE);
+ verify(mHost).setAnimateWakeup(eq(false));
}
@Test
@@ -165,49 +126,4 @@
mDozeUi.transitionTo(UNINITIALIZED, DOZE);
verify(mHost).setAnimateWakeup(eq(true));
}
-
- @Test
- public void keyguardVisibility_changesControlScreenOffAnimation() {
- // Pre-condition
- reset(mDozeParameters);
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
-
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
- verify(mDozeParameters).setControlScreenOffAnimation(eq(false));
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
- verify(mDozeParameters).setControlScreenOffAnimation(eq(true));
- }
-
- @Test
- public void transitionSetsAnimateWakeup_noAlwaysOn() {
- mDozeUi.transitionTo(UNINITIALIZED, DOZE);
- verify(mHost).setAnimateWakeup(eq(false));
- }
-
- @Test
- public void controlScreenOffTrueWhenKeyguardNotShowingAndControlUnlockedScreenOff() {
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
-
- // Tell doze that keyguard is not visible.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false /* showing */);
-
- // Since we're controlling the unlocked screen off animation, verify that we've asked to
- // control the screen off animation despite being unlocked.
- verify(mDozeParameters).setControlScreenOffAnimation(true);
- }
-
- @Test
- public void controlScreenOffFalseWhenKeyguardNotShowingAndControlUnlockedScreenOffFalse() {
- when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(false);
-
- // Tell doze that keyguard is not visible.
- mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false /* showing */);
-
- // Since we're not controlling the unlocked screen off animation, verify that we haven't
- // asked to control the screen off animation since we're unlocked.
- verify(mDozeParameters).setControlScreenOffAnimation(false);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
new file mode 100644
index 0000000..77c837b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
@@ -0,0 +1,82 @@
+package com.android.systemui.fragments
+
+import android.app.Fragment
+import android.os.Looper
+import android.test.suitebuilder.annotation.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.qs.QSFragment
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class FragmentServiceTest : SysuiTestCase() {
+ private val fragmentCreator = TestFragmentCreator()
+ private val fragmentCreatorFactory = FragmentService.FragmentCreator.Factory { fragmentCreator }
+
+ private lateinit var fragmentService: FragmentService
+
+ @Before
+ fun setUp() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare()
+ }
+
+ fragmentService = FragmentService(fragmentCreatorFactory, mock(), DumpManager())
+ }
+
+ @Test
+ fun constructor_addsFragmentCreatorMethodsToMap() {
+ val map = fragmentService.injectionMap
+ assertThat(map).hasSize(2)
+ assertThat(map.keys).contains(QSFragment::class.java.name)
+ assertThat(map.keys).contains(TestFragmentInCreator::class.java.name)
+ }
+
+ @Test
+ fun addFragmentInstantiationProvider_objectHasNoFragmentMethods_nothingAdded() {
+ fragmentService.addFragmentInstantiationProvider(Object())
+
+ assertThat(fragmentService.injectionMap).hasSize(2)
+ }
+
+ @Test
+ fun addFragmentInstantiationProvider_objectHasFragmentMethods_methodsAdded() {
+ fragmentService.addFragmentInstantiationProvider(
+ @Suppress("unused")
+ object : Any() {
+ fun createTestFragment2() = TestFragment2()
+ fun createTestFragment3() = TestFragment3()
+ }
+ )
+
+ val map = fragmentService.injectionMap
+ assertThat(map).hasSize(4)
+ assertThat(map.keys).contains(TestFragment2::class.java.name)
+ assertThat(map.keys).contains(TestFragment3::class.java.name)
+ }
+
+ @Test
+ fun addFragmentInstantiationProvider_objectFragmentMethodsAlreadyProvided_nothingAdded() {
+ fragmentService.addFragmentInstantiationProvider(
+ @Suppress("unused")
+ object : Any() {
+ fun createTestFragment() = TestFragmentInCreator()
+ }
+ )
+
+ assertThat(fragmentService.injectionMap).hasSize(2)
+ }
+
+ class TestFragmentCreator : FragmentService.FragmentCreator {
+ override fun createQSFragment(): QSFragment = mock()
+ @Suppress("unused")
+ fun createTestFragment(): TestFragmentInCreator = TestFragmentInCreator()
+ }
+
+ class TestFragmentInCreator : Fragment()
+ class TestFragment2 : Fragment()
+ class TestFragment3 : Fragment()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 3b1c5f3..e3a7e3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -54,6 +54,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.model.SysUiState;
@@ -61,6 +62,7 @@
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -115,6 +117,8 @@
@Mock private UserContextProvider mUserContextProvider;
@Mock private StatusBar mStatusBar;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
+ @Mock private SystemUIDialogManager mDialogManager;
private TestableLooper mTestableLooper;
@@ -159,8 +163,9 @@
mHandler,
mPackageManager,
Optional.of(mStatusBar),
- mKeyguardUpdateMonitor
- );
+ mKeyguardUpdateMonitor,
+ mDialogLaunchAnimator,
+ mDialogManager);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -218,7 +223,7 @@
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
- gestureListener.onSingleTapConfirmed(null);
+ gestureListener.onSingleTapUp(null);
verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
}
@@ -444,12 +449,12 @@
// When entering power menu from lockscreen, with smart lock enabled
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
- mGlobalActionsDialogLite.showOrHideDialog(true, true);
+ mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
// Then smart lock will be disabled
verify(mLockPatternUtils).requireCredentialEntry(eq(user));
// hide dialog again
- mGlobalActionsDialogLite.showOrHideDialog(true, true);
+ mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
index 61b4041..2290676 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -18,6 +18,7 @@
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
@@ -56,7 +57,8 @@
public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase {
private static final String TEST_MESSAGE = "test message";
- private static final String TEST_MESSAGE_2 = "test message 2";
+ private static final String TEST_MESSAGE_2 = "test message two";
+ private int mMsgId = 0;
@Mock
private DelayableExecutor mExecutor;
@@ -201,6 +203,24 @@
}
@Test
+ public void testSameMessage_noIndicationUpdate() {
+ // GIVEN we are showing and indication with a test message
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, createIndication(TEST_MESSAGE), true);
+ reset(mView);
+ reset(mExecutor);
+
+ // WHEN the same type tries to show the same exact message
+ final KeyguardIndication sameIndication = createIndication(TEST_MESSAGE);
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, sameIndication, true);
+
+ // THEN
+ // - we don't update the indication b/c there's no reason the animate the same text
+ verify(mView, never()).switchIndication(sameIndication);
+ }
+
+ @Test
public void testTransientIndication() {
// GIVEN we already have two indication messages
mController.updateIndication(
@@ -223,8 +243,11 @@
@Test
public void testHideIndicationOneMessage() {
// GIVEN we have one indication message
+ KeyguardIndication indication = createIndication();
mController.updateIndication(
- INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+ INDICATION_TYPE_OWNER_INFO, indication, false);
+ verify(mView).switchIndication(indication);
+ reset(mView);
// WHEN we hide the current indication type
mController.hideIndication(INDICATION_TYPE_OWNER_INFO);
@@ -254,6 +277,10 @@
@Test
public void testStartDozing() {
+ // GIVEN a biometric message is showing
+ mController.updateIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ createIndication(), true);
+
// WHEN the device is dozing
mStatusBarStateListener.onDozingChanged(true);
@@ -293,9 +320,19 @@
verify(mView, never()).switchIndication(any());
}
+ /**
+ * Create an indication with a unique message.
+ */
private KeyguardIndication createIndication() {
+ return createIndication(TEST_MESSAGE + " " + mMsgId++);
+ }
+
+ /**
+ * Create an indication with the given message.
+ */
+ private KeyguardIndication createIndication(String msg) {
return new KeyguardIndication.Builder()
- .setMessage(TEST_MESSAGE)
+ .setMessage(msg)
.setTextColor(ColorStateList.valueOf(Color.WHITE))
.build();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
new file mode 100644
index 0000000..f3043e9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -0,0 +1,134 @@
+package com.android.systemui.keyguard
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.graphics.Point
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SyncRtSurfaceTransactionApplier
+import android.view.ViewRootImpl
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardViewController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+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.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
+ private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+
+ @Mock
+ private lateinit var keyguardViewMediator: KeyguardViewMediator
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var keyguardViewController: KeyguardViewController
+ @Mock
+ private lateinit var smartspaceTransitionController: SmartspaceTransitionController
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock
+ private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
+
+ private lateinit var remoteAnimationTarget: RemoteAnimationTarget
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
+ context, keyguardStateController, { keyguardViewMediator }, keyguardViewController,
+ smartspaceTransitionController, featureFlags, biometricUnlockController
+ )
+
+ `when`(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
+
+ // All of these fields are final, so we can't mock them, but are needed so that the surface
+ // appear amount setter doesn't short circuit.
+ remoteAnimationTarget = RemoteAnimationTarget(
+ 0, 0, null, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
+ mock(WindowConfiguration::class.java), false, mock(SurfaceControl::class.java), Rect(),
+ mock(ActivityManager.RunningTaskInfo::class.java), false)
+
+ // Set the surface applier to our mock so that we can verify the arguments passed to it.
+ // This applier does not have any side effects within the unlock animation controller, so
+ // this is a reasonable way to test.
+ keyguardUnlockAnimationController.surfaceTransactionApplier = surfaceTransactionApplier
+ }
+
+ /**
+ * If we're wake and unlocking, we are animating from the black/AOD screen to the app/launcher
+ * underneath. The LightRevealScrim will animate circularly from the fingerprint reader,
+ * revealing the app/launcher below. In this case, we want to make sure we are not animating the
+ * surface, or the user will see the wallpaper briefly as the app animates in.
+ */
+ @Test
+ fun noSurfaceAnimation_ifWakeAndUnlocking() {
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
+
+ keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
+ verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture())
+
+ val params = captor.value
+
+ // We expect that we've instantly set the surface behind to alpha = 1f, and have no
+ // transforms (translate, scale) on its matrix.
+ assertEquals(params.alpha, 1f)
+ assertTrue(params.matrix.isIdentity)
+
+ // Also expect we've immediately asked the keyguard view mediator to finish the remote
+ // animation.
+ verify(keyguardViewMediator, times(1)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+
+ verifyNoMoreInteractions(surfaceTransactionApplier)
+ }
+
+ /**
+ * If we are not wake and unlocking, we expect the unlock animation to play normally.
+ */
+ @Test
+ fun surfaceAnimation_ifNotWakeAndUnlocking() {
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
+
+ keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ // Make sure the animator was started.
+ assertTrue(keyguardUnlockAnimationController.surfaceBehindEntryAnimator.isRunning)
+
+ // Since the animation is running, we should not have finished the remote animation.
+ verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index e01583e..d7c00fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -16,15 +16,16 @@
package com.android.systemui.keyguard;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.keyguard.LockIconView.ICON_LOCK;
import static com.android.keyguard.LockIconView.ICON_UNLOCK;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -41,7 +42,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -57,6 +57,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -66,8 +67,7 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
-import com.airbnb.lottie.LottieAnimationView;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -76,6 +76,8 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.List;
@@ -86,6 +88,8 @@
public class LockIconViewControllerTest extends SysuiTestCase {
private static final String UNLOCKED_LABEL = "unlocked";
+ private MockitoSession mStaticMockSession;
+
private @Mock LockIconView mLockIconView;
private @Mock AnimatedStateListDrawable mIconDrawable;
private @Mock Context mContext;
@@ -102,8 +106,6 @@
private @Mock ConfigurationController mConfigurationController;
private @Mock Vibrator mVibrator;
private @Mock AuthRippleController mAuthRippleController;
- private @Mock LottieAnimationView mAodFp;
- private @Mock LayoutInflater mLayoutInflater;
private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
private LockIconViewController mLockIconViewController;
@@ -133,11 +135,14 @@
@Before
public void setUp() throws Exception {
+ mStaticMockSession = mockitoSession()
+ .mockStatic(BurnInHelperKt.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
MockitoAnnotations.initMocks(this);
when(mLockIconView.getResources()).thenReturn(mResources);
when(mLockIconView.getContext()).thenReturn(mContext);
- when(mLockIconView.findViewById(R.layout.udfps_aod_lock_icon)).thenReturn(mAodFp);
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
Rect windowBounds = new Rect(0, 0, 800, 1200);
@@ -164,38 +169,13 @@
mDelayableExecutor,
mVibrator,
mAuthRippleController,
- mResources,
- mLayoutInflater
+ mResources
);
}
- @Test
- public void testIgnoreUdfpsWhenNotSupported() {
- // GIVEN Udpfs sensor is NOT available
- mLockIconViewController.init();
- captureAttachListener();
-
- // WHEN the view is attached
- mAttachListener.onViewAttachedToWindow(mLockIconView);
-
- // THEN lottie animation should NOT be inflated
- verify(mLayoutInflater, never()).inflate(eq(R.layout.udfps_aod_lock_icon), any());
- }
-
- @Test
- public void testInflateUdfpsWhenSupported() {
- // GIVEN Udpfs sensor is available
- setupUdfps();
- when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
- mLockIconViewController.init();
- captureAttachListener();
-
- // WHEN the view is attached
- mAttachListener.onViewAttachedToWindow(mLockIconView);
-
- // THEN lottie animation should be inflated
- verify(mLayoutInflater).inflate(eq(R.layout.udfps_aod_lock_icon), any());
+ @After
+ public void tearDown() {
+ mStaticMockSession.finishMocking();
}
@Test
@@ -369,6 +349,42 @@
verify(mLockIconView).updateIcon(ICON_LOCK, true);
}
+ @Test
+ public void testBurnInOffsetsUpdated_onDozeAmountChanged() {
+ // GIVEN udfps enrolled
+ setupUdfps();
+ when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
+
+ // GIVEN burn-in offset = 5
+ int burnInOffset = 5;
+ when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset);
+
+ // GIVEN starting state for the lock icon (keyguard)
+ setupShowLockIcon();
+ mLockIconViewController.init();
+ captureAttachListener();
+ mAttachListener.onViewAttachedToWindow(mLockIconView);
+ captureStatusBarStateListener();
+ reset(mLockIconView);
+
+ // WHEN dozing updates
+ mStatusBarStateListener.onDozingChanged(true /* isDozing */);
+ mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
+
+ // THEN the view's translation is updated to use the AoD burn-in offsets
+ verify(mLockIconView).setTranslationY(burnInOffset);
+ verify(mLockIconView).setTranslationX(burnInOffset);
+ reset(mLockIconView);
+
+ // WHEN the device is no longer dozing
+ mStatusBarStateListener.onDozingChanged(false /* isDozing */);
+ mStatusBarStateListener.onDozeAmountChanged(0f, 0f);
+
+ // THEN the view is updated to NO translation (no burn-in offsets anymore)
+ verify(mLockIconView).setTranslationY(0);
+ verify(mLockIconView).setTranslationX(0);
+
+ }
private Pair<Integer, PointF> setupUdfps() {
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
final PointF udfpsLocation = new PointF(50, 75);
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 175ec87f..a6e567e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -104,37 +104,54 @@
fun testPlayerOrdering() {
// Test values: key, data, last active time
val playingLocal = Triple("playing local",
- DATA.copy(active = true, isPlaying = true, isLocalSession = true, resumption = false),
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
4500L)
- val playingRemote = Triple("playing remote",
- DATA.copy(active = true, isPlaying = true, isLocalSession = false, resumption = false),
+ val playingCast = Triple("playing cast",
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL, resumption = false),
5000L)
val pausedLocal = Triple("paused local",
- DATA.copy(active = true, isPlaying = false, isLocalSession = true, resumption = false),
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
1000L)
- val pausedRemote = Triple("paused remote",
- DATA.copy(active = true, isPlaying = false, isLocalSession = false, resumption = false),
+ val pausedCast = Triple("paused cast",
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL, resumption = false),
2000L)
+ val playingRcn = Triple("playing RCN",
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_CAST_REMOTE, resumption = false),
+ 5000L)
+
+ val pausedRcn = Triple("paused RCN",
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_CAST_REMOTE, resumption = false),
+ 5000L)
+
val resume1 = Triple("resume 1",
- DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ DATA.copy(active = false, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
500L)
val resume2 = Triple("resume 2",
- DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ DATA.copy(active = false, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
1000L)
// Expected ordering for media players:
// Actively playing local sessions
- // Actively playing remote sessions
- // Paused sessions, by last active
+ // Actively playing cast sessions
+ // Paused local and cast sessions, by last active
+ // RCNs
// Resume controls, by last active
- val expected = listOf(playingLocal, playingRemote, pausedRemote, pausedLocal, resume2,
- resume1)
+ val expected = listOf(playingLocal, playingCast, pausedCast, pausedLocal, playingRcn,
+ pausedRcn, resume2, resume1)
expected.forEach {
clock.setCurrentTimeMillis(it.third)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index bbeadf6..41ce941 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -123,6 +123,7 @@
private lateinit var session: MediaSession
private val device = MediaDeviceData(true, null, DEVICE_NAME)
private val disabledDevice = MediaDeviceData(false, null, "Disabled Device")
+ private val clock = FakeSystemClock()
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -134,7 +135,7 @@
player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil,
- mediaOutputDialogFactory, mediaCarouselController, falsingManager)
+ mediaOutputDialogFactory, mediaCarouselController, falsingManager, clock)
whenever(seekBarViewModel.progress).thenReturn(seekBarData)
// Mock out a view holder for the player to attach to.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 66b6470..09c83e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.eq;
@@ -74,8 +75,8 @@
mManager.addListener(mListener);
mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
- new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true,
- false, KEY, false, false, false, 0L);
+ new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null,
+ MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
}
@@ -83,10 +84,10 @@
public void eventNotEmittedWithoutDevice() {
// WHEN data source emits an event without device data
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
// THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(),
- anyBoolean());
+ anyInt());
}
@Test
@@ -95,7 +96,7 @@
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(),
- anyBoolean());
+ anyInt());
}
@Test
@@ -104,11 +105,11 @@
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN media event is received
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(),
- anyBoolean());
+ anyInt());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@@ -116,13 +117,13 @@
public void emitEventAfterMediaFirst() {
// GIVEN that media event has already been received
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
// WHEN device event is received
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(),
- anyBoolean());
+ anyInt());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@@ -130,16 +131,16 @@
public void migrateKeyMediaFirst() {
// GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
reset(mListener);
// WHEN a key migration event is received
mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(),
- anyBoolean());
+ anyInt());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@@ -147,7 +148,7 @@
public void migrateKeyDeviceFirst() {
// GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
reset(mListener);
// WHEN a key migration event is received
@@ -155,7 +156,7 @@
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(),
- anyBoolean());
+ anyInt());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@@ -163,17 +164,17 @@
public void migrateKeyMediaAfter() {
// GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
reset(mListener);
// WHEN a second key migration event is received for media
mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
// THEN the key has already been migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(),
- anyBoolean());
+ anyInt());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@@ -181,17 +182,17 @@
public void migrateKeyDeviceAfter() {
// GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
reset(mListener);
// WHEN a second key migration event is received for the device
mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
// THEN the key has already be migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(),
- anyBoolean());
+ anyInt());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@@ -206,7 +207,7 @@
@Test
public void mediaDataRemovedAfterMediaEvent() {
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY));
}
@@ -222,14 +223,14 @@
public void mediaDataKeyUpdated() {
// GIVEN that device and media events have already been received
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN the key is changed
mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */,
- false /* isSsReactivated */);
+ 0 /* receivedSmartspaceCardLatency */);
// THEN the listener gets a load event with the correct keys
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(
- eq("NEW_KEY"), any(), captor.capture(), anyBoolean(), anyBoolean());
+ eq("NEW_KEY"), any(), captor.capture(), anyBoolean(), anyInt());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index a435e79..5a3c43c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -87,7 +87,7 @@
fun setup() {
MockitoAnnotations.initMocks(this)
MediaPlayerData.clear()
- mediaDataFilter = MediaDataFilter(context, broadcastDispatcher, mediaResumeListener,
+ mediaDataFilter = MediaDataFilter(context, broadcastDispatcher,
lockscreenUserManager, executor, clock)
mediaDataFilter.mediaDataManager = mediaDataManager
mediaDataFilter.addListener(listener)
@@ -107,6 +107,8 @@
`when`(smartspaceData.isValid).thenReturn(true)
`when`(smartspaceData.packageName).thenReturn(PACKAGE)
`when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem))
+ `when`(smartspaceData.headphoneConnectionTimeMillis).thenReturn(
+ clock.currentTimeMillis() - 100)
}
private fun setUser(id: Int) {
@@ -122,7 +124,7 @@
// THEN we should tell the listener
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -131,7 +133,7 @@
mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
// THEN we should NOT tell the listener
- verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean())
+ verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt())
}
@Test
@@ -178,11 +180,11 @@
// THEN we should add back the guest user media
verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true),
- eq(false))
+ eq(0))
// but not the main user's
verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(),
- anyBoolean())
+ anyInt())
}
@Test
@@ -239,7 +241,8 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener)
- .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true),
+ eq(false))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
}
@@ -249,8 +252,9 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean())
- verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
+ verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt())
+ verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean(),
+ anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
@@ -262,7 +266,8 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener)
- .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true),
+ eq(true))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
}
@@ -275,7 +280,8 @@
clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
+ verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean(),
+ anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
@@ -287,15 +293,16 @@
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
- eq(false))
+ eq(0))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should tell listeners to treat the media as not active instead
verify(listener, never()).onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(),
+ anyInt())
+ verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean(),
anyBoolean())
- verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
@@ -307,7 +314,7 @@
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
- eq(false))
+ eq(0))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -315,10 +322,11 @@
// THEN we should tell listeners to treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
- eq(true))
+ eq(100))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
// Smartspace update shouldn't be propagated for the empty rec list.
- verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
+ verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean(),
+ anyBoolean())
}
@Test
@@ -327,7 +335,7 @@
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
- eq(false))
+ eq(0))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -335,11 +343,12 @@
// THEN we should tell listeners to treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
- eq(true))
+ eq(100))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
// Smartspace update should also be propagated but not prioritized.
verify(listener)
- .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false),
+ eq(true))
}
@Test
@@ -356,13 +365,13 @@
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
- eq(false))
+ eq(0))
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
- eq(true))
+ eq(100))
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 2b2fc51..e2019e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media
+import android.app.Notification
import android.app.Notification.MediaStyle
import android.app.PendingIntent
import android.app.smartspace.SmartspaceAction
@@ -159,6 +160,7 @@
whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem))
+ whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
}
@After
@@ -212,7 +214,7 @@
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -224,11 +226,35 @@
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
assertThat(mediaDataCaptor.value!!.active).isTrue()
}
@Test
+ fun testOnNotificationAdded_isRcn_markedRemote() {
+ val bundle = Bundle().apply {
+ putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Remote Cast Notification")
+ }
+ val rcn = SbnBuilder().run {
+ setPkg("com.android.systemui") // System package
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ it.addExtras(bundle)
+ }
+ build()
+ }
+
+ mediaDataManager.onNotificationAdded(KEY, rcn)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(0))
+ assertThat(mediaDataCaptor.value!!.playbackLocation).isEqualTo(
+ MediaData.PLAYBACK_CAST_REMOTE)
+ }
+
+ @Test
fun testOnNotificationRemoved_callsListener() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
@@ -244,7 +270,7 @@
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -253,7 +279,7 @@
// THEN the media data indicates that it is for resumption
verify(listener)
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
assertThat(mediaDataCaptor.value.resumption).isTrue()
assertThat(mediaDataCaptor.value.isPlaying).isFalse()
}
@@ -268,7 +294,7 @@
assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
val resumableData = data.copy(resumeAction = Runnable {})
@@ -280,7 +306,7 @@
// THEN the data is for resumption and the key is migrated to the package name
verify(listener)
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
assertThat(mediaDataCaptor.value.resumption).isTrue()
verify(listener, never()).onMediaDataRemoved(eq(KEY))
// WHEN the second is removed
@@ -289,7 +315,7 @@
verify(listener)
.onMediaDataLoaded(
eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
assertThat(mediaDataCaptor.value.resumption).isTrue()
verify(listener).onMediaDataRemoved(eq(KEY_2))
}
@@ -304,9 +330,10 @@
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
val data = mediaDataCaptor.value
- val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, isLocalSession = false)
+ val dataRemoteWithResume = data.copy(resumeAction = Runnable {},
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume)
// WHEN the notification is removed
@@ -331,7 +358,7 @@
// THEN the media data indicates that it is for resumption
verify(listener)
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
val data = mediaDataCaptor.value
assertThat(data.resumption).isTrue()
assertThat(data.song).isEqualTo(SESSION_TITLE)
@@ -379,7 +406,7 @@
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -389,8 +416,8 @@
eq(KEY_MEDIA_SMARTSPACE),
eq(SmartspaceMediaData(KEY_MEDIA_SMARTSPACE, true /* isActive */, true /*isValid */,
PACKAGE_NAME, mediaSmartspaceBaseAction, listOf(mediaRecommendationItem),
- DISMISS_INTENT, 0)),
- eq(false))
+ DISMISS_INTENT, 0, 1234L)),
+ eq(false), eq(false))
}
@Test
@@ -401,8 +428,9 @@
eq(KEY_MEDIA_SMARTSPACE),
eq(EMPTY_SMARTSPACE_MEDIA_DATA
.copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
- isValid = false, dismissIntent = DISMISS_INTENT)),
- eq(false))
+ isValid = false, dismissIntent = DISMISS_INTENT,
+ headphoneConnectionTimeMillis = 1234L)),
+ eq(false), eq(false))
}
@Test
@@ -421,15 +449,15 @@
eq(KEY_MEDIA_SMARTSPACE),
eq(EMPTY_SMARTSPACE_MEDIA_DATA
.copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
- isValid = false, dismissIntent = null)),
- eq(false))
+ isValid = false, dismissIntent = null, headphoneConnectionTimeMillis = 1234L)),
+ eq(false), eq(false))
}
@Test
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_notCallsListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf())
verify(listener, never())
- .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean())
+ .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean(), anyBoolean())
}
@Test
@@ -453,7 +481,7 @@
// THEN smartspace signal is ignored
verify(listener, never())
- .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean())
+ .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean(), anyBoolean())
}
@Test
@@ -461,7 +489,7 @@
// GIVEN a media recommendation card is present
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
verify(listener).onSmartspaceMediaDataLoaded(eq(KEY_MEDIA_SMARTSPACE), anyObject(),
- anyBoolean())
+ anyBoolean(), anyBoolean())
// WHEN the media recommendation setting is turned off
Settings.Secure.putInt(context.contentResolver,
@@ -481,7 +509,7 @@
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTime)
}
@@ -499,7 +527,7 @@
// THEN the last active time is not changed
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
}
@@ -511,7 +539,7 @@
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -524,7 +552,7 @@
// THEN the last active time is not changed
verify(listener)
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
assertThat(mediaDataCaptor.value.resumption).isTrue()
assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
}
@@ -551,7 +579,7 @@
// THEN only the first MAX_COMPACT_ACTIONS are actually set
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
- eq(false))
+ eq(0))
assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo(
MediaDataManager.MAX_COMPACT_ACTIONS)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index bf87a4a..a3ffb2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -22,6 +22,7 @@
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -82,6 +83,8 @@
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock
private lateinit var configurationController: ConfigurationController
+ @Mock
+ private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
@@ -94,6 +97,8 @@
@Before
fun setup() {
+ context.getOrCreateTestableResources().addOverride(
+ R.bool.config_use_split_notification_shade, false)
mediaFrame = FrameLayout(context)
`when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
mediaHiearchyManager = MediaHierarchyManager(
@@ -124,7 +129,7 @@
private fun setupHost(host: MediaHost, location: Int) {
`when`(host.location).thenReturn(location)
`when`(host.currentBounds).thenReturn(Rect())
- `when`(host.hostView).thenReturn(UniqueObjectHostView(context))
+ `when`(host.hostView).thenReturn(uniqueObjectHostView)
`when`(host.visible).thenReturn(true)
mediaHiearchyManager.register(host)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index 8dc9eff..421f9be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -42,7 +42,8 @@
val mockito = MockitoJUnit.rule()
companion object {
- val LOCAL = true
+ val LOCAL = MediaData.PLAYBACK_LOCAL
+ val REMOTE = MediaData.PLAYBACK_CAST_LOCAL
val RESUMPTION = true
val PLAYING = true
val UNDETERMINED = null
@@ -58,7 +59,7 @@
val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsRemote = mock(MediaControlPanel::class.java)
- val dataIsRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsRemote = createMediaData("app2", PLAYING, REMOTE, !RESUMPTION)
MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote, systemClock)
MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying, systemClock)
@@ -100,13 +101,13 @@
val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java)
- val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, REMOTE, !RESUMPTION)
val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java)
val dataIsStoppedAndLocal = createMediaData("app3", !PLAYING, LOCAL, !RESUMPTION)
val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java)
- val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, REMOTE, !RESUMPTION)
val playerCanResume = mock(MediaControlPanel::class.java)
val dataCanResume = createMediaData("app5", !PLAYING, LOCAL, RESUMPTION)
@@ -127,8 +128,8 @@
val players = MediaPlayerData.players()
assertThat(players).hasSize(6)
assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote,
- playerIsStoppedAndRemote, playerIsStoppedAndLocal, playerCanResume,
- playerUndetermined).inOrder()
+ playerIsStoppedAndRemote, playerIsStoppedAndLocal, playerUndetermined,
+ playerCanResume).inOrder()
}
@Test
@@ -160,9 +161,10 @@
private fun createMediaData(
app: String,
isPlaying: Boolean?,
- isLocalSession: Boolean,
+ location: Int,
resumption: Boolean
) =
- MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(), "",
- null, null, null, true, null, isLocalSession, resumption, null, false, isPlaying)
+ MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(),
+ "package:" + app, null, null, null, true, null, location, resumption, "key:" + app,
+ false, isPlaying)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 359746b..54da1a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -211,10 +211,20 @@
}
@Test
- fun testOnLoad_remotePlayback_doesNotCheck() {
- // When media data is loaded that has not been checked yet, and is not local
- val dataRemote = data.copy(isLocalSession = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataRemote)
+ fun testOnLoad_localCast_doesNotCheck() {
+ // When media data is loaded that has not been checked yet, and is a local cast
+ val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
+ resumeListener.onMediaDataLoaded(KEY, null, dataCast)
+
+ // Then we do not take action
+ verify(mediaDataManager, never()).setResumeAction(any(), any())
+ }
+
+ @Test
+ fun testOnload_remoteCast_doesNotCheck() {
+ // When media data is loaded that has not been checked yet, and is a remote cast
+ val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE)
+ resumeListener.onMediaDataLoaded(KEY, null, dataRcn)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
index b9caab2..5d53181 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
@@ -37,6 +37,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.any
@@ -186,7 +187,7 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -209,7 +210,7 @@
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -239,7 +240,7 @@
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -255,14 +256,14 @@
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
// WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(KEY, null, mediaData2)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is filtered
verify(mediaListener, never()).onMediaDataLoaded(
- eq(KEY), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
+ eq(KEY), eq(null), eq(mediaData2), anyBoolean(), anyInt())
}
@Test
@@ -279,7 +280,7 @@
// THEN the event is not filtered because there isn't a notification for the remote
// session.
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -297,14 +298,14 @@
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
// WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(key2, null, mediaData2)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is filtered
verify(mediaListener, never())
- .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
+ .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyInt())
// AND there should be a removed event for key2
verify(mediaListener).onMediaDataRemoved(eq(key2))
}
@@ -324,14 +325,14 @@
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
// WHEN a loaded event is received that matches the remote session
filter.onMediaDataLoaded(key2, null, mediaData2)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -348,14 +349,14 @@
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
// WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(KEY, null, mediaData2)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -374,7 +375,7 @@
fgExecutor.runAllReady()
// THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -397,7 +398,7 @@
fgExecutor.runAllReady()
// THEN the key migration event is fired
verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true),
- eq(false))
+ eq(0))
}
@Test
@@ -427,13 +428,13 @@
fgExecutor.runAllReady()
// THEN the key migration event is filtered
verify(mediaListener, never())
- .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
+ .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyInt())
// WHEN a loaded event is received that matches the remote session
filter.onMediaDataLoaded(key2, null, mediaData1)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the key migration event is fired
verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true),
- eq(false))
+ eq(0))
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 053851e..451291d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.Before;
import org.junit.Test;
@@ -65,6 +66,7 @@
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
private MediaOutputController mMediaOutputController;
@@ -77,7 +79,7 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
@@ -167,7 +169,7 @@
class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
- super(context, mediaOutputController);
+ super(context, mediaOutputController, mDialogManager);
mAdapter = mMediaOutputBaseAdapter;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 09ec4ca..cd26e0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -54,6 +54,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.Before;
import org.junit.Test;
@@ -93,6 +94,7 @@
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private Context mSpyContext;
private MediaOutputController mMediaOutputController;
@@ -115,7 +117,7 @@
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -159,7 +161,7 @@
public void start_withoutPackageName_verifyMediaControllerInit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputController.start(mCb);
@@ -180,7 +182,7 @@
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputController.start(mCb);
@@ -451,7 +453,7 @@
public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 8a3ea56..ada8d35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.Before;
@@ -67,6 +68,7 @@
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
@@ -76,10 +78,10 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false,
- mMediaOutputController, mUiEventLogger);
+ mMediaOutputController, mUiEventLogger, mDialogManager);
mMediaOutputDialog.show();
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
@@ -125,7 +127,7 @@
// and verify if the calling times increases.
public void onCreate_ShouldLogVisibility() {
MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false,
- mMediaOutputController, mUiEventLogger);
+ mMediaOutputController, mUiEventLogger, mDialogManager);
testDialog.show();
testDialog.dismissDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index e8cd6c8..b114452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.Before;
@@ -66,6 +67,7 @@
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputGroupDialog mMediaOutputGroupDialog;
private MediaOutputController mMediaOutputController;
@@ -75,10 +77,10 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
- mMediaOutputController);
+ mMediaOutputController, mDialogManager);
mMediaOutputGroupDialog.show();
when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
new file mode 100644
index 0000000..a445d6f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.systemui.navigationbar;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+import dagger.Lazy;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NavBarHelperTest extends SysuiTestCase {
+
+ @Mock
+ AccessibilityManager mAccessibilityManager;
+ @Mock
+ AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ @Mock
+ AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ @Mock
+ OverviewProxyService mOverviewProxyService;
+ @Mock
+ Lazy<AssistManager> mAssistManagerLazy;
+ @Mock
+ AssistManager mAssistManager;
+ @Mock
+ NavigationModeController mNavigationModeController;
+ @Mock
+ UserTracker mUserTracker;
+ @Mock
+ ComponentName mAssistantComponent;
+ @Mock
+ DumpManager mDumpManager;
+ @Mock
+ NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater;
+
+ private NavBarHelper mNavBarHelper;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mAssistManagerLazy.get()).thenReturn(mAssistManager);
+ when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent);
+ when(mUserTracker.getUserId()).thenReturn(1);
+
+ mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
+ mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver,
+ mOverviewProxyService, mAssistManagerLazy, () -> Optional.of(mock(StatusBar.class)),
+ mNavigationModeController, mUserTracker, mDumpManager);
+
+ }
+
+ @Test
+ public void registerListenersInCtor() {
+ verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
+ verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
+ verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
+ }
+
+ @Test
+ public void registerAssistantContentObserver() {
+ mNavBarHelper.init();
+ verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
+ }
+
+ @Test
+ public void callbacksFiredWhenRegistering() {
+ mNavBarHelper.init();
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterConnecting() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onConnectionChanged(false);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+
+ mNavBarHelper.onConnectionChanged(true);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void a11yCallbacksFiredAfterModeChange() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterNavModeChange() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onNavigationModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void removeListenerNoCallbacksFired() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Remove listener
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Would have fired 2nd callback if not removed
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 4fc329f..3e8e874 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -45,7 +45,9 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.pip.Pip;
import org.junit.After;
import org.junit.Before;
@@ -54,6 +56,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/** atest NavigationBarControllerTest */
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -82,11 +86,13 @@
mCommandQueue,
Dependency.get(Dependency.MAIN_HANDLER),
mock(ConfigurationController.class),
- mock(NavigationBarA11yHelper.class),
+ mock(NavBarHelper.class),
mock(TaskbarDelegate.class),
mNavigationBarFactory,
mock(DumpManager.class),
- mock(AutoHideController.class)));
+ mock(AutoHideController.class),
+ mock(LightBarController.class),
+ Optional.of(mock(Pip.class))));
initializeNavigationBars();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 223ffbd..5003013 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -23,17 +23,20 @@
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.WindowInsets.Type.ime;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -57,6 +60,7 @@
import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
@@ -72,6 +76,7 @@
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -83,8 +88,10 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -98,6 +105,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.util.Optional;
@@ -130,8 +138,7 @@
EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
@Mock
EdgeBackGestureHandler mEdgeBackGestureHandler;
- @Mock
- NavigationBarA11yHelper mNavigationBarA11yHelper;
+ NavBarHelper mNavBarHelper;
@Mock
private LightBarController mLightBarController;
@Mock
@@ -148,6 +155,8 @@
private InputMethodManager mInputMethodManager;
@Mock
private AssistManager mAssistManager;
+ @Mock
+ private StatusBar mStatusBar;
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -172,6 +181,12 @@
mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
TestableLooper.get(this).runWithLooper(() -> {
+ mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
+ mock(AccessibilityManagerWrapper.class),
+ mock(AccessibilityButtonModeObserver.class), mOverviewProxyService,
+ () -> mock(AssistManager.class), () -> Optional.of(mStatusBar),
+ mock(NavigationModeController.class), mock(UserTracker.class),
+ mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
@@ -220,6 +235,7 @@
new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
.setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
.build());
+ when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
@@ -255,6 +271,11 @@
// Create default & external NavBar fragment.
NavigationBar defaultNavBar = mNavigationBar;
NavigationBar externalNavBar = mExternalDisplayNavigationBar;
+ NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
+ WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
+ doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+ doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+ doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
doNothing().when(defaultNavBar).checkNavBarModes();
doNothing().when(externalNavBar).checkNavBarModes();
defaultNavBar.createView(null);
@@ -281,17 +302,51 @@
}
@Test
+ public void testSetImeWindowStatusWhenKeyguardLockingAndImeInsetsChange() {
+ NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
+ doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+ doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
+ doNothing().when(mNavigationBar).checkNavBarModes();
+ mNavigationBar.createView(null);
+ WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
+ doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+
+ // Verify navbar altered back icon when an app is showing IME
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+
+ // Verify navbar didn't alter and showing back icon when the keyguard is showing without
+ // requesting IME insets visible.
+ doReturn(true).when(mStatusBar).isKeyguardShowing();
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true);
+ assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+ assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+
+ // Verify navbar altered and showing back icon when the keyguard is showing and
+ // requesting IME insets visible.
+ windowInsets = new WindowInsets.Builder().setVisible(ime(), true).build();
+ doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ }
+
+ @Test
public void testA11yEventAfterDetach() {
View v = mNavigationBar.createView(null);
mNavigationBar.onViewAttachedToWindow(v);
- verify(mNavigationBarA11yHelper).registerA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).registerNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
mNavigationBar.onViewDetachedFromWindow(v);
- verify(mNavigationBarA11yHelper).removeA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).removeNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
// Should be safe even though the internal view is now null.
- mNavigationBar.updateAccessibilityServicesState();
+ mNavigationBar.updateAcessibilityStateFlags();
}
private NavigationBar createNavBar(Context context) {
@@ -313,7 +368,7 @@
Optional.of(mock(Pip.class)),
Optional.of(mock(LegacySplitScreen.class)),
Optional.of(mock(Recents.class)),
- () -> Optional.of(mock(StatusBar.class)),
+ () -> Optional.of(mStatusBar),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
@@ -321,7 +376,7 @@
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
- mNavigationBarA11yHelper,
+ mNavBarHelper,
mock(UserTracker.class),
mLightBarController,
mLightBarcontrollerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
new file mode 100644
index 0000000..92743ae5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -0,0 +1,128 @@
+package com.android.systemui.qs
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyDialogController
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var privacyItemController: PrivacyItemController
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+ @Mock
+ private lateinit var privacyChip: OngoingPrivacyChip
+ @Mock
+ private lateinit var privacyDialogController: PrivacyDialogController
+ @Mock
+ private lateinit var privacyLogger: PrivacyLogger
+ @Mock
+ private lateinit var iconContainer: StatusIconContainer
+
+ private lateinit var cameraSlotName: String
+ private lateinit var microphoneSlotName: String
+ private lateinit var locationSlotName: String
+
+ private lateinit var controller: HeaderPrivacyIconsController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(privacyChip.context).thenReturn(context)
+ whenever(privacyChip.resources).thenReturn(context.resources)
+
+ cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
+ microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
+ locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
+
+ controller = HeaderPrivacyIconsController(
+ privacyItemController,
+ uiEventLogger,
+ privacyChip,
+ privacyDialogController,
+ privacyLogger,
+ iconContainer
+ )
+ }
+
+ @Test
+ fun testIgnoredSlotsOnParentVisible_noIndicators() {
+ setPrivacyController(micCamera = false, location = false)
+
+ controller.onParentVisible()
+
+ verify(iconContainer).removeIgnoredSlot(cameraSlotName)
+ verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).removeIgnoredSlot(locationSlotName)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnParentVisible_onlyMicCamera() {
+ setPrivacyController(micCamera = true, location = false)
+
+ controller.onParentVisible()
+
+ verify(iconContainer).addIgnoredSlot(cameraSlotName)
+ verify(iconContainer).addIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).removeIgnoredSlot(locationSlotName)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnParentVisible_onlyLocation() {
+ setPrivacyController(micCamera = false, location = true)
+
+ controller.onParentVisible()
+
+ verify(iconContainer).removeIgnoredSlot(cameraSlotName)
+ verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).addIgnoredSlot(locationSlotName)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnParentVisible_locationMicCamera() {
+ setPrivacyController(micCamera = true, location = true)
+
+ controller.onParentVisible()
+
+ verify(iconContainer).addIgnoredSlot(cameraSlotName)
+ verify(iconContainer).addIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).addIgnoredSlot(locationSlotName)
+ }
+
+ @Test
+ fun testPrivacyChipClicked() {
+ controller.onParentVisible()
+
+ val captor = argumentCaptor<View.OnClickListener>()
+ verify(privacyChip).setOnClickListener(capture(captor))
+
+ captor.value.onClick(privacyChip)
+
+ verify(privacyDialogController).showDialog(any(Context::class.java))
+ }
+
+ private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
+ whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
+ whenever(privacyItemController.locationAvailable).thenReturn(location)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 30664ba..5b9a7f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -173,8 +173,8 @@
protected Fragment instantiate(Context context, String className, Bundle arguments) {
CommandQueue commandQueue = new CommandQueue(context);
return new QSFragment(
- new RemoteInputQuickSettingsDisabler(context, mock(ConfigurationController.class),
- commandQueue),
+ new RemoteInputQuickSettingsDisabler(context, commandQueue,
+ mock(ConfigurationController.class)),
mock(QSTileHost.class),
mock(StatusBarStateController.class),
commandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
index f41d7b1..e2a0626 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
@@ -1,7 +1,6 @@
package com.android.systemui.qs
import android.testing.AndroidTestingRunner
-import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Before
@@ -9,7 +8,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -18,13 +16,9 @@
@SmallTest
class QSSquishinessControllerTest : SysuiTestCase() {
- @Mock private lateinit var qqsFooterActionsView: FooterActionsView
- @Mock private lateinit var qqsFooterActionsViewLP: ViewGroup.MarginLayoutParams
@Mock private lateinit var qsAnimator: QSAnimator
@Mock private lateinit var qsPanelController: QSPanelController
@Mock private lateinit var quickQsPanelController: QuickQSPanelController
- @Mock private lateinit var tileLayout: TileLayout
- @Mock private lateinit var pagedTileLayout: PagedTileLayout
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@@ -32,11 +26,8 @@
@Before
fun setup() {
- qsSquishinessController = QSSquishinessController(qqsFooterActionsView, qsAnimator,
+ qsSquishinessController = QSSquishinessController(qsAnimator,
qsPanelController, quickQsPanelController)
- `when`(quickQsPanelController.tileLayout).thenReturn(tileLayout)
- `when`(qsPanelController.tileLayout).thenReturn(pagedTileLayout)
- `when`(qqsFooterActionsView.layoutParams).thenReturn(qqsFooterActionsViewLP)
}
@Test
@@ -51,7 +42,7 @@
@Test
fun setSquishiness_updatesTiles() {
qsSquishinessController.squishiness = 0.5f
- verify(tileLayout).setSquishinessFraction(0.5f)
- verify(pagedTileLayout).setSquishinessFraction(0.5f)
+ verify(qsPanelController).setSquishinessFraction(0.5f)
+ verify(quickQsPanelController).setSquishinessFraction(0.5f)
}
}
\ No newline at end of file
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 f85167e..62b6725 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -20,18 +20,12 @@
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.privacy.OngoingPrivacyChip
-import com.android.systemui.privacy.PrivacyDialogController
-import com.android.systemui.privacy.PrivacyItemController
-import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -51,10 +45,10 @@
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -64,11 +58,7 @@
@Mock
private lateinit var view: QuickStatusBarHeader
@Mock
- private lateinit var privacyItemController: PrivacyItemController
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var uiEventLogger: UiEventLogger
+ private lateinit var privacyIconsController: HeaderPrivacyIconsController
@Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
@@ -80,18 +70,12 @@
@Mock
private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock
- private lateinit var privacyLogger: PrivacyLogger
- @Mock
private lateinit var colorExtractor: SysuiColorExtractor
@Mock
private lateinit var iconContainer: StatusIconContainer
@Mock
private lateinit var qsCarrierGroup: QSCarrierGroup
@Mock
- private lateinit var privacyChip: OngoingPrivacyChip
- @Mock
- private lateinit var privacyDialogController: PrivacyDialogController
- @Mock
private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
@Mock
private lateinit var variableDateViewController: VariableDateViewController
@@ -114,10 +98,6 @@
private lateinit var controller: QuickStatusBarHeaderController
- private lateinit var cameraSlotName: String
- private lateinit var microphoneSlotName: String
- private lateinit var locationSlotName: String
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -130,25 +110,14 @@
`when`(view.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context)
- cameraSlotName = mContext.resources.getString(
- com.android.internal.R.string.status_bar_camera)
- microphoneSlotName = mContext.resources.getString(
- com.android.internal.R.string.status_bar_microphone)
- locationSlotName = mContext.resources.getString(
- com.android.internal.R.string.status_bar_location)
-
controller = QuickStatusBarHeaderController(
view,
- privacyItemController,
- activityStarter,
- uiEventLogger,
+ privacyIconsController,
statusBarIconController,
demoModeController,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
- privacyLogger,
colorExtractor,
- privacyDialogController,
qsExpansionPathInterpolator,
batteryMeterViewController,
featureFlags,
@@ -168,62 +137,6 @@
}
@Test
- fun testIgnoredSlotsOnAttached_noIndicators() {
- setPrivacyController(micCamera = false, location = false)
-
- controller.init()
-
- verify(iconContainer).removeIgnoredSlot(cameraSlotName)
- verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
- verify(iconContainer).removeIgnoredSlot(locationSlotName)
- }
-
- @Test
- fun testIgnoredSlotsOnAttached_onlyMicCamera() {
- setPrivacyController(micCamera = true, location = false)
-
- controller.init()
-
- verify(iconContainer).addIgnoredSlot(cameraSlotName)
- verify(iconContainer).addIgnoredSlot(microphoneSlotName)
- verify(iconContainer).removeIgnoredSlot(locationSlotName)
- }
-
- @Test
- fun testIgnoredSlotsOnAttached_onlyLocation() {
- setPrivacyController(micCamera = false, location = true)
-
- controller.init()
-
- verify(iconContainer).removeIgnoredSlot(cameraSlotName)
- verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
- verify(iconContainer).addIgnoredSlot(locationSlotName)
- }
-
- @Test
- fun testIgnoredSlotsOnAttached_locationMicCamera() {
- setPrivacyController(micCamera = true, location = true)
-
- controller.init()
-
- verify(iconContainer).addIgnoredSlot(cameraSlotName)
- verify(iconContainer).addIgnoredSlot(microphoneSlotName)
- verify(iconContainer).addIgnoredSlot(locationSlotName)
- }
-
- @Test
- fun testPrivacyChipClicked() {
- controller.init()
-
- val captor = argumentCaptor<View.OnClickListener>()
- verify(privacyChip).setOnClickListener(capture(captor))
-
- captor.value.onClick(privacyChip)
-
- verify(privacyDialogController).showDialog(any(Context::class.java))
- }
-
- @Test
fun testSingleCarrierListenerAttachedOnInit() {
controller.init()
@@ -292,14 +205,8 @@
`when`(view.findViewById<View>(anyInt())).thenReturn(mockView)
`when`(view.findViewById<QSCarrierGroup>(R.id.carrier_group)).thenReturn(qsCarrierGroup)
`when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer)
- `when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip)
`when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
`when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView)
`when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView)
}
-
- private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
- `when`(privacyItemController.micCameraAvailable).thenReturn(micCamera)
- `when`(privacyItemController.locationAvailable).thenReturn(location)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 5a49337..b40a20c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -38,6 +38,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -90,6 +91,8 @@
private HotspotController.Callback mHotspotCallback;
@Mock
private QSLogger mQSLogger;
+ @Mock
+ private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -113,7 +116,8 @@
mController,
mKeyguard,
mNetworkController,
- mHotspotController
+ mHotspotController,
+ mDialogLaunchAnimator
);
mCastTile.initialize();
@@ -241,6 +245,7 @@
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
+ when(mKeyguard.isShowing()).thenReturn(true);
enableWifiAndProcessMessages();
mCastTile.handleClick(null /* view */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index f99703e..9936d49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -16,22 +16,28 @@
package com.android.systemui.qs.tiles
+import android.app.Dialog
import android.content.ContextWrapper
import android.content.SharedPreferences
import android.os.Handler
import android.provider.Settings
+import android.provider.Settings.Global.ZEN_MODE_OFF
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
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.statusbar.policy.ZenModeController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
@@ -40,9 +46,12 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.io.File
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -70,6 +79,10 @@
private lateinit var zenModeController: ZenModeController
@Mock
private lateinit var sharedPreferences: SharedPreferences
+ @Mock
+ private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock
+ private lateinit var hostDialog: Dialog
private lateinit var secureSettings: SecureSettings
private lateinit var testableLooper: TestableLooper
@@ -81,15 +94,15 @@
testableLooper = TestableLooper.get(this)
secureSettings = FakeSettings()
- Mockito.`when`(qsHost.userId).thenReturn(DEFAULT_USER)
- Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+ whenever(qsHost.userId).thenReturn(DEFAULT_USER)
+ whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger)
val wrappedContext = object : ContextWrapper(context) {
override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
return sharedPreferences
}
}
- Mockito.`when`(qsHost.context).thenReturn(wrappedContext)
+ whenever(qsHost.context).thenReturn(wrappedContext)
tile = DndTile(
qsHost,
@@ -102,7 +115,8 @@
qsLogger,
zenModeController,
sharedPreferences,
- secureSettings
+ secureSettings,
+ dialogLaunchAnimator
)
}
@@ -147,4 +161,32 @@
assertThat(tile.state.forceExpandIcon).isTrue()
}
+
+ @Test
+ fun testLaunchDialogFromViewWhenPrompt() {
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+
+ secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
+ testableLooper.processAllMessages()
+
+ val view = View(context)
+ tile.handleClick(view)
+ testableLooper.processAllMessages()
+
+ verify(dialogLaunchAnimator).showFromView(any(), eq(view), anyBoolean())
+ }
+
+ @Test
+ fun testNoLaunchDialogWhenNotPrompt() {
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+
+ secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
+ testableLooper.processAllMessages()
+
+ val view = View(context)
+ tile.handleClick(view)
+ testableLooper.processAllMessages()
+
+ verify(dialogLaunchAnimator, never()).showFromView(any(), any(), anyBoolean())
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index 77946cf..d3bb241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -83,7 +85,7 @@
@Test
public void getItemCount_returnWifiEntriesCount() {
- for (int i = 0; i < InternetDialogController.MAX_WIFI_ENTRY_COUNT; i++) {
+ for (int i = 0; i < MAX_WIFI_ENTRY_COUNT; i++) {
mInternetAdapter.setWifiEntries(mWifiEntries, i /* wifiEntriesCount */);
assertThat(mInternetAdapter.getItemCount()).isEqualTo(i);
@@ -141,6 +143,60 @@
}
@Test
+ public void setWifiEntries_wifiCountLessThenMaxCount_setWifiCount() {
+ final int wifiCount = MAX_WIFI_ENTRY_COUNT - 1;
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+
+ mInternetAdapter.setWifiEntries(mWifiEntries, wifiCount);
+
+ assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(wifiCount);
+ }
+
+ @Test
+ public void setWifiEntries_wifiCountGreaterThenMaxCount_setMaxCount() {
+ final int wifiCount = MAX_WIFI_ENTRY_COUNT;
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+ mInternetAdapter.setWifiEntries(mWifiEntries, wifiCount);
+
+ assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(mInternetAdapter.mMaxEntriesCount);
+ }
+
+ @Test
+ public void setMaxEntriesCount_maxCountLessThenZero_doNothing() {
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ final int maxCount = -1;
+
+ mInternetAdapter.setMaxEntriesCount(maxCount);
+
+ assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ }
+
+ @Test
+ public void setMaxEntriesCount_maxCountGreaterThenWifiCount_updateMaxCount() {
+ mInternetAdapter.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 2;
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ final int maxCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+ mInternetAdapter.setMaxEntriesCount(maxCount);
+
+ assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(maxCount);
+ }
+
+ @Test
+ public void setMaxEntriesCount_maxCountLessThenWifiCount_updateBothCount() {
+ mInternetAdapter.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ final int maxCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+ mInternetAdapter.setMaxEntriesCount(maxCount);
+
+ assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(maxCount);
+ assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(maxCount);
+ }
+
+ @Test
public void viewHolderUpdateEndIcon_wifiConnected_updateGearIcon() {
mTestableResources.addOverride(GEAR_ICON_RES_ID, mGearIcon);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index ca8903b..0d65541 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
+import static android.provider.Settings.Global.AIRPLANE_MODE_ON;
+
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT;
@@ -19,7 +21,6 @@
import static org.mockito.Mockito.when;
import android.animation.Animator;
-import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
@@ -37,7 +38,6 @@
import android.view.View;
import android.view.WindowManager;
-import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -47,7 +47,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -70,7 +69,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -78,8 +76,6 @@
public class InternetDialogControllerTest extends SysuiTestCase {
private static final int SUB_ID = 1;
- private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
- private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
//SystemUIToast
private static final int GRAVITY_FLAGS = Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL;
@@ -142,7 +138,7 @@
private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableResources mTestableResources;
- private MockInternetDialogController mInternetDialogController;
+ private InternetDialogController mInternetDialogController;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private List<WifiEntry> mAccessPoints = new ArrayList<>();
private List<WifiEntry> mWifiEntries = new ArrayList<>();
@@ -163,14 +159,13 @@
mAccessPoints.add(mWifiEntry1);
when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{SUB_ID});
- when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
when(mToastFactory.createToast(any(), anyString(), anyString(), anyInt(), anyInt()))
.thenReturn(mSystemUIToast);
when(mSystemUIToast.getView()).thenReturn(mToastView);
when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS);
when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator);
- mInternetDialogController = new MockInternetDialogController(mContext,
+ mInternetDialogController = new InternetDialogController(mContext,
mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController,
mSubscriptionManager, mTelephonyManager, mWifiManager,
mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
@@ -225,7 +220,7 @@
@Test
public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
getResourcesString("airplane_mode")));
@@ -233,22 +228,30 @@
@Test
public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
getResourcesString("quick_settings_internet_label")));
}
@Test
- public void getSubtitleText_withAirplaneModeOn_returnNull() {
- mInternetDialogController.setAirplaneModeEnabled(true);
+ public void getSubtitleText_withApmOnAndWifiOff_returnWifiIsOff() {
+ fakeAirplaneModeEnabled(true);
+ when(mWifiManager.isWifiEnabled()).thenReturn(false);
- assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isEqualTo(getResourcesString("wifi_is_off"));
+
+ // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+ mInternetDialogController.mCanConfigWifi = false;
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isNotEqualTo(getResourcesString("wifi_is_off"));
}
@Test
public void getSubtitleText_withWifiOff_returnWifiIsOff() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(false);
assertThat(mInternetDialogController.getSubtitleText(false))
@@ -263,7 +266,7 @@
@Test
public void getSubtitleText_withNoWifiEntry_returnSearchWifi() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
@@ -280,7 +283,7 @@
@Test
public void getSubtitleText_withWifiEntry_returnTapToConnect() {
// The preconditions WiFi Entries is already in setUp()
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
assertThat(mInternetDialogController.getSubtitleText(false))
@@ -295,7 +298,7 @@
@Test
public void getSubtitleText_deviceLockedWithWifiOn_returnUnlockToViewNetworks() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
@@ -305,7 +308,7 @@
@Test
public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
@@ -319,7 +322,7 @@
@Test
public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
@@ -339,6 +342,17 @@
}
@Test
+ public void getSubtitleText_withCarrierNetworkActiveOnly_returnNoOtherAvailable() {
+ fakeAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+ when(mMergedCarrierEntry.isDefaultNetwork()).thenReturn(true);
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isEqualTo(getResourcesString("non_carrier_network_unavailable"));
+ }
+
+ @Test
public void getWifiDetailsSettingsIntent_withNoKey_returnNull() {
assertThat(mInternetDialogController.getWifiDetailsSettingsIntent(null)).isNull();
}
@@ -404,7 +418,7 @@
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
- verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any());
+ verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any(), anyBoolean());
}
@Test
@@ -413,27 +427,26 @@
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
- verify(mInternetDialogCallback)
- .onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */);
+ verify(mInternetDialogCallback).onAccessPointsChanged(null /* wifiEntries */,
+ null /* connectedEntry */, false /* hasMoreEntry */);
}
@Test
public void onAccessPointsChanged_oneConnectedEntry_callbackConnectedEntryOnly() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
+ false /* hasMoreEntry */);
}
@Test
public void onAccessPointsChanged_noConnectedEntryAndOneOther_callbackWifiEntriesOnly() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mWifiEntry1);
@@ -441,14 +454,13 @@
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
- verify(mInternetDialogCallback)
- .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
+ null /* connectedEntry */, false /* hasMoreEntry */);
}
@Test
public void onAccessPointsChanged_oneConnectedEntryAndOneOther_callbackCorrectly() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -457,13 +469,13 @@
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
+ false /* hasMoreEntry */);
}
@Test
public void onAccessPointsChanged_oneConnectedEntryAndTwoOthers_callbackCorrectly() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -474,13 +486,13 @@
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
mWifiEntries.add(mWifiEntry2);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
+ false /* hasMoreEntry */);
}
@Test
public void onAccessPointsChanged_oneConnectedEntryAndThreeOthers_callbackCutMore() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -492,52 +504,13 @@
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
mWifiEntries.add(mWifiEntry2);
- mWifiEntries.add(mWifiEntry3);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
-
- // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
- reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(false);
-
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
- mWifiEntries.remove(mWifiEntry3);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
- }
-
- @Test
- public void onAccessPointsChanged_oneConnectedEntryAndFourOthers_callbackCutMore() {
- reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
- mAccessPoints.clear();
- mAccessPoints.add(mConnectedEntry);
- mAccessPoints.add(mWifiEntry1);
- mAccessPoints.add(mWifiEntry2);
- mAccessPoints.add(mWifiEntry3);
- mAccessPoints.add(mWifiEntry4);
-
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
- mWifiEntries.clear();
- mWifiEntries.add(mWifiEntry1);
- mWifiEntries.add(mWifiEntry2);
- mWifiEntries.add(mWifiEntry3);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
-
- // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
- reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(false);
-
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
- mWifiEntries.remove(mWifiEntry3);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
+ true /* hasMoreEntry */);
}
@Test
public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mWifiEntry1);
mAccessPoints.add(mWifiEntry2);
@@ -550,29 +523,56 @@
mWifiEntries.add(mWifiEntry1);
mWifiEntries.add(mWifiEntry2);
mWifiEntries.add(mWifiEntry3);
- mWifiEntries.add(mWifiEntry4);
- verify(mInternetDialogCallback)
- .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
+ null /* connectedEntry */, true /* hasMoreEntry */);
+ }
- // If the Ethernet exists, then Wi-Fi entries will cut last one.
+ @Test
+ public void onAccessPointsChanged_wifiIsDefaultButNoInternetAccess_putIntoWifiEntries() {
reset(mInternetDialogCallback);
- mInternetDialogController.mHasEthernet = true;
+ mAccessPoints.clear();
+ when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+ when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
+ mAccessPoints.add(mWifiEntry1);
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
- mWifiEntries.remove(mWifiEntry4);
- verify(mInternetDialogCallback)
- .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
+ null /* connectedEntry */, false /* hasMoreEntry */);
+ }
- // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
- reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(false);
+ @Test
+ public void onAccessPointsChanged_connectedWifiNoInternetAccess_shouldSetListener() {
+ reset(mWifiEntry1);
+ mAccessPoints.clear();
+ when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+ when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
+ mAccessPoints.add(mWifiEntry1);
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
- mWifiEntries.remove(mWifiEntry3);
- verify(mInternetDialogCallback)
- .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+ verify(mWifiEntry1).setListener(mInternetDialogController.mConnectedWifiInternetMonitor);
+ }
+
+ @Test
+ public void onUpdated_connectedWifiHasInternetAccess_shouldScanWifiAccessPoints() {
+ reset(mAccessPointController);
+ when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+ when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
+ InternetDialogController.ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor =
+ mInternetDialogController.mConnectedWifiInternetMonitor;
+ mConnectedWifiInternetMonitor.registerCallbackIfNeed(mWifiEntry1);
+
+ // When the hasInternetAccess() changed to true, and call back the onUpdated() function.
+ when(mWifiEntry1.hasInternetAccess()).thenReturn(true);
+ mConnectedWifiInternetMonitor.onUpdated();
+
+ verify(mAccessPointController).scanForAccessPoints();
}
@Test
@@ -641,38 +641,7 @@
mContext.getPackageName());
}
- private class MockInternetDialogController extends InternetDialogController {
-
- private GlobalSettings mGlobalSettings;
- private boolean mIsAirplaneModeOn;
-
- MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
- ActivityStarter starter, AccessPointController accessPointController,
- SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
- @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
- @Main Handler handler, @Main Executor mainExecutor,
- BroadcastDispatcher broadcastDispatcher,
- KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings,
- KeyguardStateController keyguardStateController, WindowManager windowManager,
- ToastFactory toastFactory, Handler workerHandler,
- CarrierConfigTracker carrierConfigTracker,
- LocationController locationController,
- DialogLaunchAnimator dialogLaunchAnimator) {
- super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
- telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
- broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
- keyguardStateController, windowManager, toastFactory, workerHandler,
- carrierConfigTracker, locationController, dialogLaunchAnimator);
- mGlobalSettings = globalSettings;
- }
-
- @Override
- boolean isAirplaneModeEnabled() {
- return mIsAirplaneModeOn;
- }
-
- public void setAirplaneModeEnabled(boolean enabled) {
- mIsAirplaneModeOn = enabled;
- }
+ private void fakeAirplaneModeEnabled(boolean enabled) {
+ when(mGlobalSettings.getInt(eq(AIRPLANE_MODE_ON), anyInt())).thenReturn(enabled ? 1 : 0);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index b32b4d4..c20e887 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -78,6 +80,7 @@
private RecyclerView mWifiList;
private LinearLayout mSeeAll;
private LinearLayout mWifiScanNotify;
+ private TextView mAirplaneModeSummaryText;
@Before
public void setUp() {
@@ -112,6 +115,7 @@
mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAll = mDialogView.requireViewById(R.id.see_all_layout);
mWifiScanNotify = mDialogView.requireViewById(R.id.wifi_scan_notify_layout);
+ mAirplaneModeSummaryText = mDialogView.requireViewById(R.id.airplane_mode_summary);
}
@After
@@ -136,7 +140,7 @@
mInternetDialog.updateDialog(true);
- assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -189,7 +193,41 @@
}
@Test
- public void updateDialog_withApmOn_mobileDataLayoutGone() {
+ public void updateDialog_apmOffAndNotCarrierNetwork_mobileDataLayoutGone() {
+ // Mobile network should be gone if the list of active subscriptionId is null.
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDialogController.hasActiveSubId()).thenReturn(false);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayout() {
+ // Carrier network should be gone if airplane mode ON and Wi-Fi is off.
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+
+ // Carrier network should be visible if airplane mode ON and Wi-Fi is ON.
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoCarrierNetwork_mobileDataLayoutGone() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
mInternetDialog.updateDialog(true);
@@ -198,6 +236,51 @@
}
@Test
+ public void updateDialog_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ mInternetDialog.mConnectedWifiEntry = null;
+ doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ mInternetDialog.mConnectedWifiEntry = null;
+ doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_apmOffAndHasCarrierNetwork_notShowApmSummary() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoCarrierNetwork_notShowApmSummary() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
// The preconditions WiFi ON and Internet WiFi are already in setUp()
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
@@ -219,7 +302,7 @@
}
@Test
- public void updateDialog_wifiOnAndNoWifiEntry_hideWifiEntryAndSeeAll() {
+ public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
// The precondition WiFi ON is already in setUp()
mInternetDialog.mConnectedWifiEntry = null;
mInternetDialog.mWifiEntriesCount = 0;
@@ -227,42 +310,67 @@
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ // Show a blank block to fix the dialog height even if there is no WiFi list
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
}
@Test
- public void updateDialog_wifiOnAndHasConnectedWifi_showConnectedWifiAndSeeAll() {
+ public void updateDialog_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
+ // The precondition WiFi ON is already in setUp()
+ mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialog.mWifiEntriesCount = 1;
+
+ mInternetDialog.updateDialog(false);
+
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ // Show a blank block to fix the dialog height even if there is no WiFi list
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
mInternetDialog.mWifiEntriesCount = 0;
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+ // Show a blank block to fix the dialog height even if there is no WiFi list
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(2);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
}
@Test
- public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
+ public void updateDialog_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ mInternetDialog.mHasMoreWifiEntries = true;
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
+ mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+ mInternetDialog.mHasMoreWifiEntries = true;
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(2);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -412,4 +520,55 @@
assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
assertThat(mInternetDialog.mIsSearchingHidden).isTrue();
}
+
+ @Test
+ public void getWifiListMaxCount_returnCountCorrectly() {
+ // Both of the Ethernet, MobileData is hidden.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+ setNetworkVisible(false, false, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+
+ // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
+ setNetworkVisible(false, false, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ // Only one of Ethernet, MobileData is displayed.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+ setNetworkVisible(true, false, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+
+ setNetworkVisible(false, true, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+
+ // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
+ setNetworkVisible(true, false, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ setNetworkVisible(false, true, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ // Both of Ethernet, MobileData, ConnectedWiFi is displayed.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
+ setNetworkVisible(true, true, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
+ setNetworkVisible(true, true, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ }
+
+ private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
+ boolean connectedWifiVisible) {
+ mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
+ mMobileDataToggle.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
+ mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
deleted file mode 100644
index d5fe588..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.user
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.View
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class UserDialogTest : SysuiTestCase() {
-
- private lateinit var dialog: UserDialog
-
- @Before
- fun setUp() {
- dialog = UserDialog(mContext)
- }
-
- @After
- fun tearDown() {
- dialog.dismiss()
- }
-
- @Test
- fun doneButtonExists() {
- assertThat(dialog.doneButton).isInstanceOf(View::class.java)
- }
-
- @Test
- fun settingsButtonExists() {
- assertThat(dialog.settingsButton).isInstanceOf(View::class.java)
- }
-
- @Test
- fun gridExistsAndIsViewGroup() {
- assertThat(dialog.grid).isInstanceOf(ViewGroup::class.java)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index ea3a42c..b7fdc1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.user
-import android.app.Dialog
+import android.content.DialogInterface
import android.content.Intent
import android.provider.Settings
import android.testing.AndroidTestingRunner
@@ -28,8 +28,8 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PseudoGridView
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import org.junit.Before
@@ -40,10 +40,8 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.argThat
-import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -53,27 +51,19 @@
class UserSwitchDialogControllerTest : SysuiTestCase() {
@Mock
- private lateinit var dialog: UserDialog
+ private lateinit var dialog: SystemUIDialog
@Mock
private lateinit var falsingManager: FalsingManager
@Mock
- private lateinit var settingsView: View
- @Mock
- private lateinit var doneView: View
- @Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var userDetailViewAdapter: UserDetailView.Adapter
@Mock
private lateinit var launchView: View
@Mock
- private lateinit var gridView: PseudoGridView
- @Mock
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
- @Mock
- private lateinit var hostDialog: Dialog
@Captor
- private lateinit var clickCaptor: ArgumentCaptor<View.OnClickListener>
+ private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
private lateinit var controller: UserSwitchDialogController
@@ -81,13 +71,8 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(dialog.settingsButton).thenReturn(settingsView)
- `when`(dialog.doneButton).thenReturn(doneView)
- `when`(dialog.grid).thenReturn(gridView)
-
`when`(launchView.context).thenReturn(mContext)
- `when`(dialogLaunchAnimator.showFromView(any(), any(), anyBoolean()))
- .thenReturn(hostDialog)
+ `when`(dialog.context).thenReturn(mContext)
controller = UserSwitchDialogController(
{ userDetailViewAdapter },
@@ -105,30 +90,6 @@
}
@Test
- fun createCalledBeforeDoneButton() {
- controller.showDialog(launchView)
- val inOrder = inOrder(dialog)
- inOrder.verify(dialog).create()
- inOrder.verify(dialog).doneButton
- }
-
- @Test
- fun createCalledBeforeSettingsButton() {
- controller.showDialog(launchView)
- val inOrder = inOrder(dialog)
- inOrder.verify(dialog).create()
- inOrder.verify(dialog).settingsButton
- }
-
- @Test
- fun createCalledBeforeGrid() {
- controller.showDialog(launchView)
- val inOrder = inOrder(dialog)
- inOrder.verify(dialog).create()
- inOrder.verify(dialog).grid
- }
-
- @Test
fun dialog_showForAllUsers() {
controller.showDialog(launchView)
verify(dialog).setShowForAllUsers(true)
@@ -143,63 +104,44 @@
@Test
fun adapterAndGridLinked() {
controller.showDialog(launchView)
- verify(userDetailViewAdapter).linkToViewGroup(gridView)
+ verify(userDetailViewAdapter).linkToViewGroup(any<PseudoGridView>())
}
@Test
- fun clickDoneButton_dismiss() {
+ fun doneButtonSetWithNullHandler() {
controller.showDialog(launchView)
- verify(doneView).setOnClickListener(capture(clickCaptor))
-
- clickCaptor.value.onClick(doneView)
-
- verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
- verify(dialog).dismiss()
+ verify(dialog).setPositiveButton(anyInt(), eq(null))
}
@Test
- fun clickSettingsButton_noFalsing_opensSettingsAndDismisses() {
+ fun clickSettingsButton_noFalsing_opensSettings() {
`when`(falsingManager.isFalseTap(anyInt())).thenReturn(false)
controller.showDialog(launchView)
- verify(settingsView).setOnClickListener(capture(clickCaptor))
+ verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
- clickCaptor.value.onClick(settingsView)
+ clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
verify(activityStarter)
.postStartActivityDismissingKeyguard(
argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
eq(0)
)
- verify(dialog).dismiss()
}
@Test
- fun clickSettingsButton_Falsing_notOpensSettingsAndDismisses() {
+ fun clickSettingsButton_Falsing_notOpensSettings() {
`when`(falsingManager.isFalseTap(anyInt())).thenReturn(true)
controller.showDialog(launchView)
- verify(settingsView).setOnClickListener(capture(clickCaptor))
+ verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
- clickCaptor.value.onClick(settingsView)
+ clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
- verify(dialog).dismiss()
- }
-
- @Test
- fun callbackFromDialogShower_dismissesDialog() {
- val captor = argumentCaptor<UserSwitchDialogController.DialogShower>()
-
- controller.showDialog(launchView)
- verify(userDetailViewAdapter).injectDialogShower(capture(captor))
-
- captor.value.dismiss()
-
- verify(hostDialog).dismiss()
}
private class IntentMatcher(private val action: String) : ArgumentMatcher<Intent> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 91cafea..4073bb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -104,7 +104,7 @@
@Test
public void testLogStartRecording() {
- Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false);
+ Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0);
mRecordingService.onStartCommand(startIntent, 0, 0);
verify(mUiEventLogger, times(1)).log(Events.ScreenRecordEvent.SCREEN_RECORD_START);
@@ -137,7 +137,7 @@
// When the screen recording does not start properly
doThrow(new RuntimeException("fail")).when(mScreenMediaRecorder).start();
- Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false);
+ Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0);
mRecordingService.onStartCommand(startIntent, 0, 0);
// Then the state is set to not recording
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 8afefde..e427d53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -22,6 +22,7 @@
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING;
@@ -112,6 +113,8 @@
private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo",
"bar");
+ private static final int TEST_STRING_RES = R.string.keyguard_indication_trust_unlocked;
+
private String mKeyguardTryFingerprintMsg;
private String mDisclosureWithOrganization;
private String mDisclosureGeneric;
@@ -419,7 +422,7 @@
// WHEN transient text is shown
mStatusBarStateListener.onDozingChanged(true);
- mController.showTransientIndication("Test");
+ mController.showTransientIndication(TEST_STRING_RES);
// THEN wake lock is held while the animation is running
assertTrue("WakeLock expected: HELD, was: RELEASED", mWakeLock.isHeld());
@@ -434,7 +437,7 @@
// WHEN we show the transient indication
mStatusBarStateListener.onDozingChanged(true);
- mController.showTransientIndication("Test");
+ mController.showTransientIndication(TEST_STRING_RES);
// THEN wake lock is RELEASED, not held
assertFalse("WakeLock expected: RELEASED, was: HELD", mWakeLock.isHeld());
@@ -445,10 +448,11 @@
createController();
mController.setVisible(true);
- mController.showTransientIndication("Test");
+ mController.showTransientIndication(TEST_STRING_RES);
mStatusBarStateListener.onDozingChanged(true);
- assertThat(mTextView.getText()).isEqualTo("Test");
+ assertThat(mTextView.getText()).isEqualTo(
+ mContext.getResources().getString(TEST_STRING_RES));
assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
assertThat(mTextView.getAlpha()).isEqualTo(1f);
}
@@ -462,11 +466,11 @@
mController.getKeyguardCallback().onBiometricHelp(
KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
BiometricSourceType.FACE);
- verifyTransientMessage(message);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
reset(mRotateTextViewController);
mStatusBarStateListener.onDozingChanged(true);
- verifyHideIndication(INDICATION_TYPE_TRANSIENT);
+ verifyHideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE);
}
@Test
@@ -478,7 +482,7 @@
mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verifyTransientMessage(message);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
mStatusBarStateListener.onDozingChanged(true);
assertThat(mTextView.getText()).isNotEqualTo(message);
@@ -497,7 +501,8 @@
FingerprintManager.FINGERPRINT_ERROR_CANCELED, "bar",
BiometricSourceType.FINGERPRINT);
- verifyNoTransientMessage();
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ verifyNoMessage(INDICATION_TYPE_TRANSIENT);
}
@Test
@@ -757,7 +762,12 @@
verify(mRotateTextViewController).showTransient(eq(message));
}
- private void verifyNoTransientMessage() {
- verify(mRotateTextViewController, never()).showTransient(any());
+ private void verifyNoMessage(int type) {
+ if (type == INDICATION_TYPE_TRANSIENT) {
+ verify(mRotateTextViewController, never()).showTransient(anyString());
+ } else {
+ verify(mRotateTextViewController, never()).updateIndication(eq(type),
+ anyObject(), anyBoolean());
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 7938511..89435ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -6,6 +6,7 @@
import android.testing.TestableLooper.RunWithLooper
import android.util.DisplayMetrics
import com.android.systemui.ExpandHelper
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.media.MediaHierarchyManager
@@ -81,6 +82,8 @@
mDependency,
TestableLooper.get(this))
row = helper.createRow()
+ context.getOrCreateTestableResources()
+ .addOverride(R.bool.config_use_split_notification_shade, false)
transitionController = LockscreenShadeTransitionController(
statusBarStateController = statusbarStateController,
lockscreenGestureLogger = lockscreenGestureLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index a39971d..9f152e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -269,6 +269,21 @@
}
@Test
+ public void testDisableWiFiWithVcnWithUnderlyingWifi() {
+ String testSsid = "Test VCN SSID";
+ setWifiEnabled(true);
+ verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
+
+ mNetworkController.setNoNetworksAvailable(false);
+ setWifiStateForVcn(true, testSsid);
+ setWifiLevelForVcn(1);
+ verifyLastMobileDataIndicatorsForVcn(true, 1, TelephonyIcons.ICON_CWF, false);
+
+ setWifiEnabled(false);
+ verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
+ }
+
+ @Test
public void testCallStrengh() {
if (true) return;
String testSsid = "Test SSID";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index ff91978..7869b59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -213,6 +213,8 @@
// THEN the session is created
verify(smartspaceManager).createSmartspaceSession(any())
+ // THEN an event notifier is registered
+ verify(plugin).registerSmartspaceEventNotifier(any())
}
@Test
@@ -240,7 +242,7 @@
}
@Test
- fun testEmptyListIsEmittedAfterDisconnect() {
+ fun testEmptyListIsEmittedAndNotifierRemovedAfterDisconnect() {
// GIVEN a registered listener on an active session
connectSession()
clearInvocations(plugin)
@@ -249,8 +251,9 @@
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
- // THEN the listener receives an empty list of targets
+ // THEN the listener receives an empty list of targets and unregisters the notifier
verify(plugin).onTargetsAvailable(emptyList())
+ verify(plugin).registerSmartspaceEventNotifier(null)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 185d9cd..326369b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -22,6 +22,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.Assert.assertEquals;
@@ -103,6 +104,7 @@
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
@Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock private NotificationShelf mNotificationShelf;
@Before
@UiThreadTest
@@ -124,13 +126,13 @@
mDependency.injectTestDependency(GroupMembershipManager.class, mGroupMembershipManger);
mDependency.injectTestDependency(GroupExpansionManager.class, mGroupExpansionManager);
mDependency.injectTestDependency(AmbientState.class, mAmbientState);
+ mDependency.injectTestDependency(NotificationShelf.class, mNotificationShelf);
mDependency.injectTestDependency(
UnlockedScreenOffAnimationController.class, mUnlockedScreenOffAnimationController);
NotificationShelfController notificationShelfController =
mock(NotificationShelfController.class);
- NotificationShelf notificationShelf = mock(NotificationShelf.class);
- when(notificationShelfController.getView()).thenReturn(notificationShelf);
+ when(notificationShelfController.getView()).thenReturn(mNotificationShelf);
when(mNotificationSectionsManager.createSectionsForBuckets()).thenReturn(
new NotificationSection[]{
mNotificationSection
@@ -160,7 +162,7 @@
anyBoolean());
doNothing().when(mGroupExpansionManager).collapseGroups();
doNothing().when(mExpandHelper).cancelImmediately();
- doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+ doNothing().when(mNotificationShelf).setAnimationsEnabled(anyBoolean());
}
@Test
@@ -203,21 +205,43 @@
@Test
@UiThreadTest
- public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
- final float[] expectedHeight = {0f};
- final float[] expectedAppear = {0f};
+ public void testSetExpandedHeight_listenerReceivedCallbacks() {
+ final float expectedHeight = 0f;
mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> {
- Assert.assertEquals(expectedHeight[0], height, 0);
- Assert.assertEquals(expectedAppear[0], appear, .1);
+ Assert.assertEquals(expectedHeight, height, 0);
});
- expectedHeight[0] = 1f;
- expectedAppear[0] = 1f;
- mStackScroller.setExpandedHeight(expectedHeight[0]);
+ mStackScroller.setExpandedHeight(expectedHeight);
+ }
- expectedHeight[0] = 100f;
- expectedAppear[0] = 0f;
- mStackScroller.setExpandedHeight(expectedHeight[0]);
+ @Test
+ public void testAppearFractionCalculation() {
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(100);
+ // because it's the same as shelf height, appear start position equals shelf height
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(200);
+
+ assertEquals(0f, mStackScroller.calculateAppearFraction(100));
+ assertEquals(1f, mStackScroller.calculateAppearFraction(200));
+ assertEquals(0.5f, mStackScroller.calculateAppearFraction(150));
+ }
+
+ @Test
+ public void testAppearFractionCalculationIsNotNegativeWhenShelfBecomesSmaller() {
+ // this situation might occur if status bar height is defined in pixels while shelf height
+ // in dp and screen density changes - appear start position
+ // (calculated in NSSL#getMinExpansionHeight) that is adjusting for status bar might
+ // increase and become bigger that end position, which should be prevented
+
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(80);
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(90);
+
+ assertThat(mStackScroller.calculateAppearFraction(100)).isAtLeast(0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 0faf5d4..a0e91fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -438,8 +438,8 @@
assertEquals("returns false when view is null", false,
NotificationSwipeHelper.isTouchInView(mEvent, null));
- doReturn(5f).when(mEvent).getX();
- doReturn(10f).when(mEvent).getY();
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mView).getWidth();
doReturn(20).when(mView).getHeight();
@@ -455,7 +455,7 @@
assertTrue("Touch is within the view",
mSwipeHelper.isTouchInView(mEvent, mView));
- doReturn(50f).when(mEvent).getX();
+ doReturn(50f).when(mEvent).getRawX();
assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mView));
@@ -466,8 +466,8 @@
assertEquals("returns false when view is null", false,
NotificationSwipeHelper.isTouchInView(mEvent, null));
- doReturn(5f).when(mEvent).getX();
- doReturn(10f).when(mEvent).getY();
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mNotificationRow).getWidth();
doReturn(20).when(mNotificationRow).getActualHeight();
@@ -483,7 +483,7 @@
assertTrue("Touch is within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
- doReturn(50f).when(mEvent).getX();
+ doReturn(50f).when(mEvent).getRawX();
assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
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 5b60c9e..ea68143 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
@@ -4,6 +4,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
@@ -55,4 +56,24 @@
// top margin presence should decrease heads up translation up to minHeadsUpTranslation
assertThat(expandableViewState.yTranslation).isEqualTo(minHeadsUpTranslation)
}
-}
\ No newline at end of file
+
+ @Test
+ fun resetViewStates_childIsEmptyShadeView_viewIsCenteredVertically() {
+ stackScrollAlgorithm.initView(context)
+ val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
+ layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
+ }
+ hostView.removeAllViews()
+ hostView.addView(emptyShadeView)
+ ambientState.layoutMaxHeight = 1280
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ val closeHandleUnderlapHeight =
+ context.resources.getDimensionPixelSize(R.dimen.close_handle_underlap)
+ val fullHeight =
+ ambientState.layoutMaxHeight + closeHandleUnderlapHeight - ambientState.stackY
+ val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f
+ assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 25fd801..c3349f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -101,6 +102,8 @@
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
private BiometricUnlockController mBiometricUnlockController;
@Before
@@ -123,7 +126,7 @@
mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
mMetricsLogger, mDumpManager, mPowerManager,
mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
- mAuthController);
+ mAuthController, mStatusBarStateController);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
@@ -378,6 +381,22 @@
}
@Test
+ public void onUdfpsConsecutivelyFailedTwoTimes_showBouncer() {
+ // GIVEN UDFPS is supported
+ when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+
+ // WHEN udfps fails once - then don't show the bouncer
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+
+ // WHEN udfps fails the second time
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+
+ // THEN show the bouncer
+ verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ }
+
+ @Test
public void onFinishedGoingToSleep_authenticatesWhenPending() {
when(mUpdateMonitor.isGoingToSleep()).thenReturn(true);
mBiometricUnlockController.onFinishedGoingToSleep(-1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index 7a0b366..f8035ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -20,10 +20,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
@@ -34,12 +33,15 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import org.junit.Assert;
@@ -52,7 +54,6 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DozeParametersTest extends SysuiTestCase {
-
private DozeParameters mDozeParameters;
@Mock Resources mResources;
@@ -64,10 +65,31 @@
@Mock private FeatureFlags mFeatureFlags;
@Mock private DumpManager mDumpManager;
@Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private ConfigurationController mConfigurationController;
+
+ /**
+ * The current value of PowerManager's dozeAfterScreenOff property.
+ *
+ * This property controls whether System UI is controlling the screen off animation. If it's
+ * false (PowerManager should not doze after screen off) then System UI is controlling the
+ * animation. If true, we're not controlling it and PowerManager will doze immediately.
+ */
+ private boolean mPowerManagerDozeAfterScreenOff;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+
+ // Save the current value set for dozeAfterScreenOff so we can make assertions. This method
+ // is only called if the value changes, which makes it difficult to check that it was set
+ // correctly in tests.
+ doAnswer(invocation -> {
+ mPowerManagerDozeAfterScreenOff = invocation.getArgument(0);
+ return mPowerManagerDozeAfterScreenOff;
+ }).when(mPowerManager).setDozeAfterScreenOff(anyBoolean());
+
mDozeParameters = new DozeParameters(
mResources,
mAmbientDisplayConfiguration,
@@ -77,23 +99,30 @@
mTunerService,
mDumpManager,
mFeatureFlags,
- mUnlockedScreenOffAnimationController
+ mUnlockedScreenOffAnimationController,
+ mKeyguardUpdateMonitor,
+ mConfigurationController,
+ mStatusBarStateController
);
- }
- @Test
- public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
- mDozeParameters.setControlScreenOffAnimation(true);
- reset(mPowerManager);
- mDozeParameters.setControlScreenOffAnimation(false);
- verify(mPowerManager).setDozeAfterScreenOff(eq(true));
+
+ when(mFeatureFlags.useNewLockscreenAnimations()).thenReturn(true);
+
+ setAodEnabledForTest(true);
+ setShouldControlUnlockedScreenOffForTest(true);
+ setDisplayNeedsBlankingForTest(false);
}
@Test
- public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
- mDozeParameters.setControlScreenOffAnimation(false);
- reset(mPowerManager);
+ public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_correctly() {
+ // If we want to control screen off, we do NOT want PowerManager to doze after screen off.
+ // Obviously.
mDozeParameters.setControlScreenOffAnimation(true);
- verify(mPowerManager).setDozeAfterScreenOff(eq(false));
+ assertFalse(mPowerManagerDozeAfterScreenOff);
+
+ // If we don't want to control screen off, PowerManager is free to doze after screen off if
+ // that's what'll make it happy.
+ mDozeParameters.setControlScreenOffAnimation(false);
+ assertTrue(mPowerManagerDozeAfterScreenOff);
}
@Test
@@ -122,37 +151,124 @@
assertThat(mDozeParameters.getAlwaysOn()).isFalse();
}
+ /**
+ * PowerManager.setDozeAfterScreenOff(true) means we are not controlling screen off, and calling
+ * it with false means we are. Confusing, but sure - make sure that we call PowerManager with
+ * the correct value depending on whether we want to control screen off.
+ */
@Test
public void testControlUnlockedScreenOffAnimation_dozeAfterScreenOff_false() {
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
- when(mFeatureFlags.useNewLockscreenAnimations()).thenReturn(true);
- when(mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation())
- .thenReturn(true);
- assertTrue(mDozeParameters.shouldControlUnlockedScreenOff());
+ // If AOD is disabled, we shouldn't want to control screen off. Also, let's double check
+ // that when that value is updated, we called through to PowerManager.
+ setAodEnabledForTest(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ assertTrue(mPowerManagerDozeAfterScreenOff);
- // Trigger the setter for the current value.
- mDozeParameters.setControlScreenOffAnimation(mDozeParameters.shouldControlScreenOff());
-
- // We should have asked power manager not to doze after screen off no matter what, since
- // we're animating and controlling screen off.
- verify(mPowerManager).setDozeAfterScreenOff(eq(false));
+ // And vice versa...
+ setAodEnabledForTest(true);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+ assertFalse(mPowerManagerDozeAfterScreenOff);
}
@Test
public void testControlUnlockedScreenOffAnimationDisabled_dozeAfterScreenOff() {
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+ setShouldControlUnlockedScreenOffForTest(true);
when(mFeatureFlags.useNewLockscreenAnimations()).thenReturn(false);
assertFalse(mDozeParameters.shouldControlUnlockedScreenOff());
// Trigger the setter for the current value.
mDozeParameters.setControlScreenOffAnimation(mDozeParameters.shouldControlScreenOff());
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ }
- // We should have asked power manager to doze only if we're not controlling screen off
- // normally.
- verify(mPowerManager).setDozeAfterScreenOff(
- eq(!mDozeParameters.shouldControlScreenOff()));
+ @Test
+ public void propagatesAnimateScreenOff_noAlwaysOn() {
+ setAodEnabledForTest(false);
+ setDisplayNeedsBlankingForTest(false);
+
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ }
+
+ @Test
+ public void propagatesAnimateScreenOff_alwaysOn() {
+ setAodEnabledForTest(true);
+ setDisplayNeedsBlankingForTest(false);
+ setShouldControlUnlockedScreenOffForTest(false);
+
+ // Take over when the keyguard is visible.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+
+ // Do not animate screen-off when keyguard isn't visible.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ }
+
+
+ @Test
+ public void neverAnimateScreenOff_whenNotSupported() {
+ setDisplayNeedsBlankingForTest(true);
+
+ // Never animate if display doesn't support it.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ }
+
+
+ @Test
+ public void controlScreenOffTrueWhenKeyguardNotShowingAndControlUnlockedScreenOff() {
+ setShouldControlUnlockedScreenOffForTest(true);
+
+ // Tell doze that keyguard is not visible.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(
+ false /* showing */);
+
+ // Since we're controlling the unlocked screen off animation, verify that we've asked to
+ // control the screen off animation despite being unlocked.
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+ }
+
+
+ @Test
+ public void keyguardVisibility_changesControlScreenOffAnimation() {
+ setShouldControlUnlockedScreenOffForTest(false);
+
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertFalse(mDozeParameters.shouldControlScreenOff());
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+ }
+
+ @Test
+ public void keyguardVisibility_changesControlScreenOffAnimation_respectsUnlockedScreenOff() {
+ setShouldControlUnlockedScreenOffForTest(true);
+
+ // Even if the keyguard is gone, we should control screen off if we can control unlocked
+ // screen off.
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(false);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+ assertTrue(mDozeParameters.shouldControlScreenOff());
+ }
+
+ private void setDisplayNeedsBlankingForTest(boolean needsBlanking) {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_displayBlanksAfterDoze)).thenReturn(
+ needsBlanking);
+ }
+
+ private void setAodEnabledForTest(boolean enabled) {
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(enabled);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "");
+ }
+
+ private void setShouldControlUnlockedScreenOffForTest(boolean shouldControl) {
+ when(mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation())
+ .thenReturn(shouldControl);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index bafbccd..db5fd26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -162,8 +162,11 @@
}
@Test
- public void testHeaderReadFromOldController() {
- mHeadsUpAppearanceController.setAppearFraction(1.0f, 1.0f);
+ public void constructor_animationValuesUpdated() {
+ float appearFraction = .75f;
+ float expandedHeight = 400f;
+ when(mStackScrollerController.getAppearFraction()).thenReturn(appearFraction);
+ when(mStackScrollerController.getExpandedHeight()).thenReturn(expandedHeight);
HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
@@ -179,14 +182,9 @@
new View(mContext),
new View(mContext),
new View(mContext));
- newController.readFrom(mHeadsUpAppearanceController);
- Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
- newController.mExpandedHeight, 0.0f);
- Assert.assertEquals(mHeadsUpAppearanceController.mAppearFraction,
- newController.mAppearFraction, 0.0f);
- Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
- newController.mIsExpanded);
+ Assert.assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f);
+ Assert.assertEquals(appearFraction, newController.mAppearFraction, 0.0f);
}
@Test
@@ -195,7 +193,9 @@
reset(mDarkIconDispatcher);
reset(mPanelView);
reset(mStackScrollerController);
- mHeadsUpAppearanceController.destroy();
+
+ mHeadsUpAppearanceController.onViewDetached();
+
verify(mHeadsUpManager).removeListener(any());
verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
verify(mPanelView).removeTrackingHeadsUpListener(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 210744e..3257a84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -47,8 +47,8 @@
@Test
fun initFrom_doesntCrash() {
- val other = LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
+ val other = LayoutInflater.from(mContext).inflate(R.layout.keyguard_bottom_area,
+ null, false) as KeyguardBottomAreaView
other.initFrom(mKeyguardBottomArea)
other.launchVoiceAssist()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 1182695..1827c7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -62,6 +62,7 @@
private float mPanelExpansion;
private int mKeyguardStatusBarHeaderHeight;
private int mKeyguardStatusHeight;
+ private int mUserSwitchHeight;
private float mDark;
private float mQsExpansion;
private int mCutoutTopInset = 0;
@@ -264,8 +265,7 @@
@Test
public void clockPositionedDependingOnMarginInSplitShade() {
- when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
- .thenReturn(400);
+ setSplitShadeTopMargin(400);
mClockPositionAlgorithm.loadDimens(mResources);
givenLockScreen();
mIsSplitShade = true;
@@ -291,6 +291,32 @@
}
@Test
+ public void notifPaddingAccountsForMultiUserSwitcherInSplitShade() {
+ setSplitShadeTopMargin(100);
+ mUserSwitchHeight = 150;
+ mClockPositionAlgorithm.loadDimens(mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is split shade top margin + user switch height
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(250);
+ }
+
+ @Test
+ public void clockDoesntAccountForMultiUserSwitcherInSplitShade() {
+ setSplitShadeTopMargin(100);
+ mUserSwitchHeight = 150;
+ mClockPositionAlgorithm.loadDimens(mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN clockY = split shade top margin
+ assertThat(mClockPosition.clockY).isEqualTo(100);
+ }
+
+ @Test
public void notifPaddingExpandedAlignedWithClockInSplitShadeMode() {
givenLockScreen();
mIsSplitShade = true;
@@ -495,6 +521,11 @@
assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset);
}
+ private void setSplitShadeTopMargin(int value) {
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
+ .thenReturn(value);
+ }
+
private void givenHighestBurnInOffset() {
when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).then(returnsFirstArg());
}
@@ -529,7 +560,7 @@
mKeyguardStatusBarHeaderHeight,
mPanelExpansion,
mKeyguardStatusHeight,
- 0 /* userSwitchHeight */,
+ mUserSwitchHeight,
0 /* userSwitchPreferredY */,
mDark,
ZERO_DRAG,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index dc32007..7d266e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -102,7 +102,7 @@
verify(view.viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
argumentCaptor.value.onPreDraw()
- verify(moveFromCenterAnimation).onViewsReady(any(), any())
+ verify(moveFromCenterAnimation).onViewsReady(any())
}
private fun createViewMock(): PhoneStatusBarView {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
index 0cf0bd3..e45fa77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -1,14 +1,15 @@
package com.android.systemui.statusbar.phone
-import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -34,11 +35,13 @@
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var batteryMeterView: BatteryMeterView
@Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
+ @Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
private lateinit var splitShadeHeaderController: SplitShadeHeaderController
+ private lateinit var carrierIconSlots: List<String>
@Before
fun setup() {
@@ -56,14 +59,21 @@
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(featureFlags.useCombinedQSHeaders()).thenReturn(false)
- splitShadeHeaderController = SplitShadeHeaderController(view, statusBarIconController,
- qsCarrierGroupControllerBuilder, featureFlags, batteryMeterViewController)
+ splitShadeHeaderController = SplitShadeHeaderController(
+ view,
+ statusBarIconController,
+ privacyIconsController,
+ qsCarrierGroupControllerBuilder,
+ featureFlags,
+ batteryMeterViewController
+ )
+ carrierIconSlots = listOf(
+ context.getString(com.android.internal.R.string.status_bar_mobile))
}
@Test
fun setVisible_onlyInSplitShade() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
splitShadeHeaderController.splitShadeMode = false
@@ -72,17 +82,38 @@
@Test
fun updateListeners_registersWhenVisible() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
verify(qsCarrierGroupController).setListening(true)
verify(statusBarIconController).addIconGroup(any())
}
@Test
fun shadeExpandedFraction_updatesAlpha() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
splitShadeHeaderController.shadeExpandedFraction = 0.5f
verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
}
-}
\ No newline at end of file
+
+ @Test
+ fun singleCarrier_enablesCarrierIconsInStatusIcons() {
+ whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
+
+ makeShadeVisible()
+
+ verify(statusIcons).removeIgnoredSlots(carrierIconSlots)
+ }
+
+ @Test
+ fun dualCarrier_disablesCarrierIconsInStatusIcons() {
+ whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
+
+ makeShadeVisible()
+
+ verify(statusIcons).addIgnoredSlots(carrierIconSlots)
+ }
+
+ private fun makeShadeVisible() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 6f174cb..cc59b6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -374,6 +374,54 @@
}
@Test
+ public void testHideAltAuth_onShowBouncer() {
+ // GIVEN alt auth is showing
+ mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ when(mBouncer.isShowing()).thenReturn(false);
+ when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
+ reset(mAlternateAuthInterceptor);
+
+ // WHEN showBouncer is called
+ mStatusBarKeyguardViewManager.showBouncer(true);
+
+ // THEN alt bouncer should be hidden
+ verify(mAlternateAuthInterceptor).hideAlternateAuthBouncer();
+ }
+
+ @Test
+ public void testShowAltAuth_unlockingWithBiometricNotAllowed() {
+ // GIVEN alt auth exists, unlocking with biometric isn't allowed
+ mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ when(mBouncer.isShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(false);
+
+ // WHEN showGenericBouncer is called
+ final boolean scrimmed = true;
+ mStatusBarKeyguardViewManager.showGenericBouncer(scrimmed);
+
+ // THEN regular bouncer is shown
+ verify(mBouncer).show(anyBoolean(), eq(scrimmed));
+ verify(mAlternateAuthInterceptor, never()).showAlternateAuthBouncer();
+ }
+
+ @Test
+ public void testShowAltAuth_unlockingWithBiometricAllowed() {
+ // GIVEN alt auth exists, unlocking with biometric is allowed
+ mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ when(mBouncer.isShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true);
+
+ // WHEN showGenericBouncer is called
+ mStatusBarKeyguardViewManager.showGenericBouncer(true);
+
+ // THEN alt auth bouncer is shown
+ verify(mAlternateAuthInterceptor).showAlternateAuthBouncer();
+ verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testUpdateResources_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateResources();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
new file mode 100644
index 0000000..1ce7ff4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
@@ -0,0 +1,104 @@
+package com.android.systemui.statusbar.phone
+
+import android.graphics.Point
+import android.view.Display
+import android.view.Surface
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class StatusBarMoveFromCenterAnimationControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var windowManager: WindowManager
+
+ @Mock
+ private lateinit var display: Display
+
+ private val view: View = View(context)
+ private val progressProvider = TestUnfoldTransitionProvider()
+ private val scopedProvider = ScopedUnfoldTransitionProgressProvider(progressProvider)
+
+ private lateinit var controller: StatusBarMoveFromCenterAnimationController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(windowManager.defaultDisplay).thenReturn(display)
+ `when`(display.rotation).thenReturn(Surface.ROTATION_0)
+ `when`(display.getSize(any())).thenAnswer {
+ val point = it.arguments[0] as Point
+ point.x = 100
+ point.y = 100
+ Unit
+ }
+
+ scopedProvider.setReadyToHandleTransition(true)
+
+ controller = StatusBarMoveFromCenterAnimationController(scopedProvider, windowManager)
+ }
+
+ @Test
+ fun onTransitionProgressAndFinished_resetsTranslations() {
+ controller.onViewsReady(arrayOf(view))
+
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinished()
+
+ assertThat(view.translationX).isZero()
+ }
+
+ @Test
+ fun onTransitionProgress_updatesTranslations() {
+ controller.onViewsReady(arrayOf(view))
+
+ progressProvider.onTransitionProgress(0.5f)
+
+ assertThat(view.translationX).isNonZero()
+ }
+
+ @Test
+ fun onTransitionProgress_whenDetached_doesNotUpdateTranslations() {
+ controller.onViewsReady(arrayOf(view))
+ controller.onViewDetached()
+
+ progressProvider.onTransitionProgress(0.5f)
+
+ assertThat(view.translationX).isZero()
+ }
+
+ @Test
+ fun detachedAfterProgress_resetsTranslations() {
+ controller.onViewsReady(arrayOf(view))
+ progressProvider.onTransitionProgress(0.5f)
+
+ controller.onViewDetached()
+
+ assertThat(view.translationX).isZero()
+ }
+
+ @Test
+ fun transitionFinished_viewReAttached_noChangesToTranslation() {
+ controller.onViewsReady(arrayOf(view))
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinished()
+ controller.onViewDetached()
+
+ controller.onViewsReady(arrayOf(view))
+ controller.onStatusBarWidthChanged()
+
+ assertThat(view.translationX).isZero()
+ }
+}
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 72a3d66..cec5877 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
@@ -34,6 +34,7 @@
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Handler;
@@ -62,7 +63,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.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -114,6 +114,8 @@
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
+ private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ @Mock
private Handler mHandler;
@Mock
private BubblesManager mBubblesManager;
@@ -133,7 +135,7 @@
@Mock
private OnUserInteractionCallback mOnUserInteractionCallback;
@Mock
- private NotificationActivityStarter mNotificationActivityStarter;
+ private StatusBarNotificationActivityStarter mNotificationActivityStarter;
@Mock
private ActivityLaunchAnimator mActivityLaunchAnimator;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -209,7 +211,7 @@
mock(NotificationLockscreenUserManager.class),
mShadeController,
mKeyguardStateController,
- mock(NotificationInterruptStateProvider.class),
+ mNotificationInterruptStateProvider,
mock(LockPatternUtils.class),
mock(StatusBarRemoteInputCallback.class),
mActivityIntentHelper,
@@ -365,4 +367,27 @@
// Notification should not be cancelled.
verify(mEntryManager, never()).performRemoveNotification(eq(sbn), any(), anyInt());
}
+
+ @Test
+ public void testOnFullScreenIntentWhenDozing_wakeUpDevice() {
+ // GIVEN entry that can has a full screen intent that can show
+ Notification.Builder nb = new Notification.Builder(mContext, "a")
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFullScreenIntent(mock(PendingIntent.class), true);
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0,
+ "tag" + System.currentTimeMillis(), 0, 0,
+ nb.build(), new UserHandle(0), null, 0);
+ NotificationEntry entry = mock(NotificationEntry.class);
+ when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(eq(entry)))
+ .thenReturn(true);
+
+ // WHEN
+ mNotificationActivityStarter.handleFullScreenIntent(entry);
+
+ // THEN display should try wake up for the full screen intent
+ verify(mStatusBar).wakeUpForFullScreenIntent();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 88d36b0..20575ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -77,6 +77,7 @@
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -86,6 +87,7 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -132,7 +134,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -216,6 +217,7 @@
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
@Mock private NavigationBarController mNavigationBarController;
+ @Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
@Mock private BypassHeadsUpNotifier mBypassHeadsUpNotifier;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock private ColorExtractor.GradientColors mGradientColors;
@@ -244,8 +246,6 @@
@Mock private CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
@Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
@Mock private StatusBarComponent mStatusBarComponent;
- @Mock private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
- @Mock private StatusBarFragmentComponent mStatusBarFragmentComponent;
@Mock private PluginManager mPluginManager;
@Mock private LegacySplitScreen mLegacySplitScreen;
@Mock private LightsOutNotifController mLightsOutNotifController;
@@ -277,7 +277,6 @@
@Mock private StartingSurface mStartingSurface;
@Mock private OperatorNameViewController mOperatorNameViewController;
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
- @Mock private PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
@Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -350,8 +349,6 @@
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
when(mStatusBarComponentFactory.create()).thenReturn(mStatusBarComponent);
- when(mStatusBarFragmentComponentFactory.create(any()))
- .thenReturn(mStatusBarFragmentComponent);
when(mStatusBarComponent.getNotificationShadeWindowViewController()).thenReturn(
mNotificationShadeWindowViewController);
@@ -366,6 +363,7 @@
mStatusBar = new StatusBar(
mContext,
mNotificationsController,
+ mock(FragmentService.class),
mLightBarController,
mAutoHideController,
mStatusBarWindowController,
@@ -407,6 +405,7 @@
mVisualStabilityManager,
mDeviceProvisionedController,
mNavigationBarController,
+ mAccessibilityFloatingMenuController,
() -> mAssistManager,
configurationController,
mNotificationShadeWindowController,
@@ -422,7 +421,6 @@
mCommandQueue,
mCollapsedStatusBarFragmentLogger,
mStatusBarComponentFactory,
- mStatusBarFragmentComponentFactory,
mPluginManager,
Optional.of(mLegacySplitScreen),
mLightsOutNotifController,
@@ -437,7 +435,6 @@
mExtensionController,
mUserInfoControllerImpl,
mOperatorNameViewControllerFactory,
- mPhoneStatusBarViewControllerFactory,
mPhoneStatusBarPolicy,
mKeyguardIndicationController,
mDemoModeController,
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 ec35edf..b97f053 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
@@ -21,7 +21,6 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
import android.app.Fragment;
@@ -48,9 +47,9 @@
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
@@ -62,12 +61,11 @@
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.Mockito;
-
-import java.util.Optional;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -82,16 +80,22 @@
// Set in instantiate()
private StatusBarIconController mStatusBarIconController;
private NetworkController mNetworkController;
- private StatusBarStateController mStatusBarStateController;
private KeyguardStateController mKeyguardStateController;
- private final StatusBar mStatusBar = mock(StatusBar.class);
private final CommandQueue mCommandQueue = mock(CommandQueue.class);
private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private OperatorNameViewController mOperatorNameViewController;
+ @Mock
private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
+ @Mock
private StatusBarFragmentComponent mStatusBarFragmentComponent;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ @Mock
+ private NotificationPanelViewController mNotificationPanelViewController;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -99,49 +103,35 @@
@Before
public void setup() {
- mStatusBarStateController = mDependency
- .injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- when(mStatusBar.getPanelController()).thenReturn(
- mock(NotificationPanelViewController.class));
}
@Test
- public void testDisableNone() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableNone() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
- .getVisibility());
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
- public void testDisableSystemInfo() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableSystemInfo() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
- assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
}
@Test
- public void testDisableNotifications() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableNotifications() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
@@ -153,25 +143,21 @@
}
@Test
- public void testDisableClock() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableClock() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
- assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
public void disable_noOngoingCall_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
@@ -183,9 +169,7 @@
@Test
public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -199,9 +183,7 @@
@Test
public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -214,9 +196,7 @@
@Test
public void disable_ongoingCallEnded_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -234,41 +214,95 @@
mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
}
- @Ignore("b/192618546")
@Test
- public void testOnDozingChanged() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
-
- fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
-
- Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
-
- reset(mStatusBarStateController);
+ public void disable_isDozingButNoCustomClock_clockAndSystemInfoVisible() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(false);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_customClockButNotDozing_clockAndSystemInfoVisible() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_dozingAndCustomClock_clockAndSystemInfoHidden() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+ // Make sure they start out as visible
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void onDozingChanged_clockAndSystemInfoVisibilitiesUpdated() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+ // Make sure they start out as visible
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+
fragment.onDozingChanged(true);
- Mockito.verify(mStatusBarStateController).isDozing();
- Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ // When this callback is triggered, we want to make sure the clock and system info
+ // visibilities are recalculated. Since dozing=true, they shouldn't be visible.
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
public void setUp_fragmentCreatesDaggerComponent() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent());
}
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
- mStatusBarFragmentComponentFactory =
- mock(StatusBarFragmentComponent.Factory.class);
- mStatusBarFragmentComponent = mock(StatusBarFragmentComponent.class);
- when(mStatusBarFragmentComponentFactory.create(any()))
- .thenReturn(mStatusBarFragmentComponent);
+ MockitoAnnotations.initMocks(this);
+ setUpDaggerComponent();
mOngoingCallController = mock(OngoingCallController.class);
mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
mLocationPublisher = mock(StatusBarLocationPublisher.class);
@@ -290,11 +324,11 @@
mMockNotificationAreaController,
new PanelExpansionStateManager(),
mock(FeatureFlags.class),
- () -> Optional.of(mStatusBar),
mStatusBarIconController,
new StatusBarHideIconsForBouncerManager(
mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
mKeyguardStateController,
+ mNotificationPanelViewController,
mNetworkController,
mStatusBarStateController,
mCommandQueue,
@@ -305,6 +339,13 @@
mOperatorNameViewControllerFactory);
}
+ private void setUpDaggerComponent() {
+ when(mStatusBarFragmentComponentFactory.create(any()))
+ .thenReturn(mStatusBarFragmentComponent);
+ when(mStatusBarFragmentComponent.getHeadsUpAppearanceController())
+ .thenReturn(mHeadsUpAppearanceController);
+ }
+
private void setUpNotificationIconAreaController() {
mMockNotificationAreaController = mock(NotificationIconAreaController.class);
@@ -323,4 +364,18 @@
when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
mNotificationAreaInner);
}
+
+ private CollapsedStatusBarFragment resumeAndGetFragment() {
+ mFragments.dispatchResume();
+ processAllMessages();
+ return (CollapsedStatusBarFragment) mFragment;
+ }
+
+ private View getClockView() {
+ return mFragment.getView().findViewById(R.id.clock);
+ }
+
+ private View getSystemIconAreaView() {
+ return mFragment.getView().findViewById(R.id.system_icon_area);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
deleted file mode 100644
index b359b9c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
+++ /dev/null
@@ -1,95 +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.policy;
-
-import static junit.framework.TestCase.assertTrue;
-
-import static org.junit.Assert.assertFalse;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RemoteInputQuickSettingsDisablerTest extends SysuiTestCase {
-
- @Mock
- private CommandQueue mCommandQueue;
- private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mRemoteInputQuickSettingsDisabler = new RemoteInputQuickSettingsDisabler(mContext,
- mock(ConfigurationController.class), mCommandQueue);
- }
-
- @Test
- public void shouldEnableQuickSetting_afterDeactiviate() {
- mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
- mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
- assertFalse(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
- }
-
- @Test
- public void shouldDisableQuickSetting_afteActiviate() {
- mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
- mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
- assertTrue(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
- }
-
- @Test
- public void testChangeToLandscape() {
- Configuration c = new Configuration(mContext.getResources().getConfiguration());
- c.orientation = Configuration.ORIENTATION_PORTRAIT;
- mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
- c.orientation = Configuration.ORIENTATION_LANDSCAPE;
- mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
- assertTrue(mRemoteInputQuickSettingsDisabler.misLandscape);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
- }
-
- @Test
- public void testChangeToPortrait() {
- Configuration c = new Configuration(mContext.getResources().getConfiguration());
- c.orientation = Configuration.ORIENTATION_LANDSCAPE;
- mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
- c.orientation = Configuration.ORIENTATION_PORTRAIT;
- mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
- assertFalse(mRemoteInputQuickSettingsDisabler.misLandscape);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
- }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
new file mode 100644
index 0000000..1ab0582
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.policy
+
+import android.app.StatusBarManager
+import android.content.res.Configuration
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.CommandQueue
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class RemoteInputQuickSettingsDisablerTest : SysuiTestCase() {
+
+ @Mock lateinit var commandQueue: CommandQueue
+ private lateinit var remoteInputQuickSettingsDisabler: RemoteInputQuickSettingsDisabler
+ private lateinit var configuration: Configuration
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ remoteInputQuickSettingsDisabler = RemoteInputQuickSettingsDisabler(
+ mContext,
+ commandQueue, Mockito.mock(ConfigurationController::class.java)
+ )
+ configuration = Configuration(mContext.resources.configuration)
+
+ // Default these conditions to what they need to be to disable QS.
+ mContext.orCreateTestableResources
+ .addOverride(R.bool.config_use_split_notification_shade, /* value= */false)
+ remoteInputQuickSettingsDisabler.setRemoteInputActive(true)
+ configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+ }
+
+ @Test
+ fun whenRemoteInputActiveAndLandscapeAndNotSplitShade_shouldDisableQs() {
+ assertThat(
+ shouldDisableQs(
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(0)
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun whenRemoteInputNotActive_shouldNotDisableQs() {
+ remoteInputQuickSettingsDisabler.setRemoteInputActive(false)
+
+ assertThat(
+ shouldDisableQs(
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(0)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun whenSplitShadeEnabled_shouldNotDisableQs() {
+ mContext.orCreateTestableResources
+ .addOverride(R.bool.config_use_split_notification_shade, /* value= */true)
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+
+ assertThat(
+ shouldDisableQs(
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(0)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun whenPortrait_shouldNotDisableQs() {
+ configuration.orientation = Configuration.ORIENTATION_PORTRAIT
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+
+ assertThat(
+ shouldDisableQs(
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(0)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun whenRemoteInputChanges_recomputeTriggered() {
+ remoteInputQuickSettingsDisabler.setRemoteInputActive(false)
+
+ verify(commandQueue, atLeastOnce()).recomputeDisableFlags(
+ anyInt(), anyBoolean()
+ )
+ }
+
+ @Test
+ fun whenConfigChanges_recomputeTriggered() {
+ configuration.orientation = Configuration.ORIENTATION_PORTRAIT
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+
+ verify(commandQueue, atLeastOnce()).recomputeDisableFlags(
+ anyInt(), anyBoolean()
+ )
+ }
+
+ private fun shouldDisableQs(state: Int): Boolean {
+ return state and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index bdd189a..a4bf142 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -31,6 +31,7 @@
import android.os.UserManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.ThreadedRenderer
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
@@ -39,6 +40,7 @@
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
@@ -63,6 +65,8 @@
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -90,6 +94,8 @@
@Mock private lateinit var latencyTracker: LatencyTracker
@Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
@Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
+ @Mock private lateinit var threadedRenderer: ThreadedRenderer
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private lateinit var testableLooper: TestableLooper
private lateinit var uiBgExecutor: FakeExecutor
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -143,9 +149,20 @@
uiBgExecutor,
interactionJankMonitor,
latencyTracker,
- dumpManager)
+ dumpManager,
+ dialogLaunchAnimator)
userSwitcherController.mPauseRefreshUsers = true
+ // Since userSwitcherController involves InteractionJankMonitor.
+ // Let's fulfill the dependencies.
+ val mockedContext = mock(Context::class.java)
+ doReturn(mockedContext).`when`(notificationShadeWindowView).context
+ doReturn(true).`when`(notificationShadeWindowView).isAttachedToWindow
+ doNothing().`when`(threadedRenderer).addObserver(any())
+ doNothing().`when`(threadedRenderer).removeObserver(any())
+ doReturn(threadedRenderer).`when`(notificationShadeWindowView).threadedRenderer
+ userSwitcherController.init(notificationShadeWindowView)
+
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
userSwitcherController.init(notificationShadeWindowView)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 3ff5666..b357c78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -76,6 +76,9 @@
@RunWith(AndroidTestingRunner.class)
public class ThemeOverlayControllerTest extends SysuiTestCase {
+ private static final int USER_SYSTEM = UserHandle.USER_SYSTEM;
+ private static final int USER_SECONDARY = 10;
+
private ThemeOverlayController mThemeOverlayController;
@Mock
private Executor mBgExecutor;
@@ -111,6 +114,9 @@
private ArgumentCaptor<DeviceProvisionedListener> mDeviceProvisionedListener;
@Captor
private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessLifecycleObserver;
+ @Captor
+ private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallback;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -133,6 +139,7 @@
mWakefulnessLifecycle.dispatchFinishedWakingUp();
mThemeOverlayController.start();
+ verify(mUserTracker).addCallback(mUserTrackerCallback.capture(), eq(mMainExecutor));
verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null),
eq(UserHandle.USER_ALL));
verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiver.capture(), any(),
@@ -156,7 +163,8 @@
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
ArgumentCaptor.forClass(Map.class);
@@ -170,12 +178,13 @@
.isEqualTo(new OverlayIdentifier("ffff0000"));
// Should not ask again if changed to same value
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
verifyNoMoreInteractions(mThemeOverlayApplier);
// Should not ask again even for new colors until we change wallpapers
mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
- null, null), WallpaperManager.FLAG_SYSTEM);
+ null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
verifyNoMoreInteractions(mThemeOverlayApplier);
// But should change theme after changing wallpapers
@@ -184,7 +193,7 @@
intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
mBroadcastReceiver.getValue().onReceive(null, intent);
mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
- null, null), WallpaperManager.FLAG_SYSTEM);
+ null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
}
@@ -193,7 +202,8 @@
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
ArgumentCaptor.forClass(Map.class);
@@ -211,7 +221,7 @@
clearInvocations(mThemeOverlayApplier);
mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
- null, null), WallpaperManager.FLAG_SYSTEM);
+ null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
verify(mThemeOverlayApplier, never())
.applyCurrentUserOverlays(any(), any(), anyInt(), any());
}
@@ -229,7 +239,8 @@
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
ArgumentCaptor.forClass(Map.class);
@@ -257,7 +268,8 @@
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
verify(mSecureSettings).putString(
@@ -289,10 +301,13 @@
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(20);
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM)).thenReturn(21);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(20);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+ .thenReturn(21);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
verify(mSecureSettings).putString(
@@ -320,10 +335,11 @@
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(-1);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(-1);
mColorsListener.getValue().onColorsChanged(mainColors,
- WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
+ WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK, USER_SYSTEM);
ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
verify(mSecureSettings).putString(
@@ -349,9 +365,11 @@
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(1);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(1);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_LOCK);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_LOCK,
+ USER_SYSTEM);
ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
verify(mSecureSettings).putString(
@@ -377,9 +395,11 @@
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(-1);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(-1);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
verify(mSecureSettings).putString(
@@ -407,11 +427,14 @@
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(1);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(1);
// SYSTEM wallpaper is the last applied one
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM)).thenReturn(2);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+ .thenReturn(2);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
verify(mSecureSettings).putString(
@@ -437,11 +460,14 @@
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(1);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(1);
// SYSTEM wallpaper is the last applied one
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM)).thenReturn(2);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+ .thenReturn(2);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
verify(mSecureSettings, never()).putString(
@@ -467,11 +493,14 @@
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(1);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(1);
// SYSTEM wallpaper is the last applied one
- when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM)).thenReturn(2);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+ .thenReturn(2);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_LOCK);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_LOCK,
+ USER_SYSTEM);
verify(mSecureSettings, never()).putString(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), any());
@@ -482,6 +511,34 @@
}
@Test
+ public void onUserSwitching_setsTheme() {
+ // Setup users with different colors
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), null, null);
+ WallpaperColors secondaryColors =
+ new WallpaperColors(Color.valueOf(Color.BLUE), null, null);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(secondaryColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SECONDARY);
+
+ // When changing users
+ clearInvocations(mThemeOverlayApplier);
+ when(mUserTracker.getUserId()).thenReturn(USER_SECONDARY);
+ mUserTrackerCallback.getValue().onUserChanged(USER_SECONDARY, mContext);
+
+ ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
+ ArgumentCaptor.forClass(Map.class);
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+
+ // Assert that we received secondary user colors
+ assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
+ .isEqualTo(new OverlayIdentifier("ff0000ff"));
+ assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
+ .isEqualTo(new OverlayIdentifier("ff0000ff"));
+ }
+
+ @Test
public void onProfileAdded_setsTheme() {
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
@@ -515,7 +572,8 @@
reset(mDeviceProvisionedController);
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
@@ -525,11 +583,11 @@
Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
mBroadcastReceiver.getValue().onReceive(null, intent);
- mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
any());
mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN),
- null, null), WallpaperManager.FLAG_SYSTEM);
+ null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
any());
}
@@ -607,7 +665,8 @@
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
// Defers event because we already have initial colors.
verify(mThemeOverlayApplier, never())
@@ -628,14 +687,16 @@
// Second color application is not applied.
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
clearInvocations(mThemeOverlayApplier);
// Device went to sleep and second set of colors was applied.
mainColors = new WallpaperColors(Color.valueOf(Color.BLUE),
Color.valueOf(Color.RED), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
verify(mThemeOverlayApplier, never())
.applyCurrentUserOverlays(any(), any(), anyInt(), any());
@@ -652,14 +713,16 @@
// Second color application is not applied.
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
clearInvocations(mThemeOverlayApplier);
// Device went to sleep and second set of colors was applied.
mainColors = new WallpaperColors(Color.valueOf(Color.BLUE),
Color.valueOf(Color.RED), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
verify(mThemeOverlayApplier, never())
.applyCurrentUserOverlays(any(), any(), anyInt(), any());
@@ -678,7 +741,8 @@
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
ArgumentCaptor.forClass(Map.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index a1d9a7b..be1720d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -18,7 +18,9 @@
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.os.Handler
import android.testing.AndroidTestingRunner
+import androidx.core.util.Consumer
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
@@ -31,9 +33,12 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.lang.Exception
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -48,16 +53,28 @@
@Mock
private lateinit var deviceStateManager: DeviceStateManager
- private lateinit var foldStateProvider: FoldStateProvider
+ @Mock
+ private lateinit var handler: Handler
+
+ @Captor
+ private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+
+ @Captor
+ private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
+
+ @Captor
+ private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
+
+ private lateinit var foldStateProvider: DeviceFoldStateProvider
private val foldUpdates: MutableList<Int> = arrayListOf()
private val hingeAngleUpdates: MutableList<Float> = arrayListOf()
- private val foldStateListenerCaptor = ArgumentCaptor.forClass(FoldStateListener::class.java)
private var foldedDeviceState: Int = 0
private var unfoldedDeviceState: Int = 0
- private val screenOnListenerCaptor = ArgumentCaptor.forClass(ScreenListener::class.java)
+ private var scheduledRunnable: Runnable? = null
+ private var scheduledRunnableDelay: Long? = null
@Before
fun setUp() {
@@ -75,7 +92,8 @@
hingeAngleProvider,
screenStatusProvider,
deviceStateManager,
- context.mainExecutor
+ context.mainExecutor,
+ handler
)
foldStateProvider.addCallback(object : FoldStateProvider.FoldUpdatesListener {
@@ -91,6 +109,22 @@
verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
verify(screenStatusProvider).addCallback(screenOnListenerCaptor.capture())
+ verify(hingeAngleProvider).addCallback(hingeAngleCaptor.capture())
+
+ whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock ->
+ scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
+ scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
+ null
+ }
+
+ whenever(handler.removeCallbacks(any<Runnable>())).then { invocationOnMock ->
+ val removedRunnable = invocationOnMock.getArgument<Runnable>(0)
+ if (removedRunnable == scheduledRunnable) {
+ scheduledRunnableDelay = null
+ scheduledRunnable = null
+ }
+ null
+ }
}
@Test
@@ -167,6 +201,86 @@
assertThat(foldUpdates).isEmpty()
}
+ @Test
+ fun startClosingEvent_afterTimeout_abortEmitted() {
+ sendHingeAngleEvent(90)
+ sendHingeAngleEvent(80)
+
+ simulateTimeout(ABORT_CLOSING_MILLIS)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+ }
+
+ @Test
+ fun startClosingEvent_beforeTimeout_abortNotEmitted() {
+ sendHingeAngleEvent(90)
+ sendHingeAngleEvent(80)
+
+ simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ }
+
+ @Test
+ fun startClosingEvent_eventBeforeTimeout_oneEventEmitted() {
+ sendHingeAngleEvent(180)
+ sendHingeAngleEvent(90)
+
+ simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+ sendHingeAngleEvent(80)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ }
+
+ @Test
+ fun startClosingEvent_timeoutAfterTimeoutRescheduled_abortEmitted() {
+ sendHingeAngleEvent(180)
+ sendHingeAngleEvent(90)
+
+ simulateTimeout(ABORT_CLOSING_MILLIS - 1) // The timeout should not trigger here.
+ sendHingeAngleEvent(80)
+ simulateTimeout(ABORT_CLOSING_MILLIS) // The timeout should trigger here.
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+ }
+
+ @Test
+ fun startClosingEvent_shortTimeBetween_emitsOnlyOneEvents() {
+ sendHingeAngleEvent(180)
+
+ sendHingeAngleEvent(90)
+ sendHingeAngleEvent(80)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ }
+
+ @Test
+ fun startClosingEvent_whileClosing_emittedDespiteInitialAngle() {
+ val maxAngle = 180 - FULLY_OPEN_THRESHOLD_DEGREES.toInt()
+ for (i in 1..maxAngle) {
+ foldUpdates.clear()
+
+ simulateFolding(startAngle = i)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ simulateTimeout() // Timeout to set the state to aborted.
+ }
+ }
+
+ private fun simulateTimeout(waitTime: Long = ABORT_CLOSING_MILLIS) {
+ val runnableDelay = scheduledRunnableDelay ?: throw Exception("No runnable scheduled.")
+ if (waitTime >= runnableDelay) {
+ scheduledRunnable?.run()
+ scheduledRunnable = null
+ scheduledRunnableDelay = null
+ }
+ }
+
+ private fun simulateFolding(startAngle: Int) {
+ sendHingeAngleEvent(startAngle)
+ sendHingeAngleEvent(startAngle - 1)
+ }
+
private fun setFoldState(folded: Boolean) {
val state = if (folded) foldedDeviceState else unfoldedDeviceState
foldStateListenerCaptor.value.onStateChanged(state)
@@ -175,4 +289,8 @@
private fun fireScreenOnEvent() {
screenOnListenerCaptor.value.onScreenTurnedOn()
}
+
+ private fun sendHingeAngleEvent(angle: Int) {
+ hingeAngleCaptor.value.accept(angle.toFloat())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
new file mode 100644
index 0000000..db7a8516
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.systemui.unfold.util
+
+import android.animation.ValueAnimator
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.util.mockito.any
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
+
+ @Mock
+ lateinit var contentResolver: ContentResolver
+
+ @Mock
+ lateinit var sourceProvider: UnfoldTransitionProgressProvider
+
+ @Mock
+ lateinit var sinkProvider: TransitionProgressListener
+
+ lateinit var progressProvider: ScaleAwareTransitionProgressProvider
+
+ private val sourceProviderListenerCaptor =
+ ArgumentCaptor.forClass(TransitionProgressListener::class.java)
+
+ private val animatorDurationScaleListenerCaptor =
+ ArgumentCaptor.forClass(ContentObserver::class.java)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ progressProvider = ScaleAwareTransitionProgressProvider(
+ sourceProvider,
+ contentResolver
+ )
+
+ verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
+ verify(contentResolver).registerContentObserver(any(), any(),
+ animatorDurationScaleListenerCaptor.capture())
+
+ progressProvider.addCallback(sinkProvider)
+ }
+
+ @Test
+ fun onTransitionStarted_animationsEnabled_eventReceived() {
+ setAnimationsEnabled(true)
+
+ source.onTransitionStarted()
+
+ verify(sinkProvider).onTransitionStarted()
+ }
+
+ @Test
+ fun onTransitionStarted_animationsNotEnabled_eventNotReceived() {
+ setAnimationsEnabled(false)
+
+ source.onTransitionStarted()
+
+ verifyNoMoreInteractions(sinkProvider)
+ }
+
+ @Test
+ fun onTransitionEnd_animationsEnabled_eventReceived() {
+ setAnimationsEnabled(true)
+
+ source.onTransitionFinished()
+
+ verify(sinkProvider).onTransitionFinished()
+ }
+
+ @Test
+ fun onTransitionEnd_animationsNotEnabled_eventNotReceived() {
+ setAnimationsEnabled(false)
+
+ source.onTransitionFinished()
+
+ verifyNoMoreInteractions(sinkProvider)
+ }
+
+ @Test
+ fun onTransitionProgress_animationsEnabled_eventReceived() {
+ setAnimationsEnabled(true)
+
+ source.onTransitionProgress(42f)
+
+ verify(sinkProvider).onTransitionProgress(42f)
+ }
+
+ @Test
+ fun onTransitionProgress_animationsNotEnabled_eventNotReceived() {
+ setAnimationsEnabled(false)
+
+ source.onTransitionProgress(42f)
+
+ verifyNoMoreInteractions(sinkProvider)
+ }
+
+ private fun setAnimationsEnabled(enabled: Boolean) {
+ val durationScale = if (enabled) {
+ 1f
+ } else {
+ 0f
+ }
+ ValueAnimator.setDurationScale(durationScale)
+ animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
+ }
+
+ private val source: TransitionProgressListener
+ get() = sourceProviderListenerCaptor.value
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index ae7afce..2f2e536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -35,13 +35,14 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.compatui.CompatUI;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
@@ -77,8 +78,9 @@
@Mock WakefulnessLifecycle mWakefulnessLifecycle;
@Mock ProtoTracer mProtoTracer;
@Mock ShellCommandHandler mShellCommandHandler;
- @Mock SizeCompatUI mSizeCompatUI;
+ @Mock CompatUI mCompatUI;
@Mock ShellExecutor mSysUiMainExecutor;
+ @Mock DragAndDrop mDragAndDrop;
@Before
public void setUp() {
@@ -86,7 +88,8 @@
mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
- Optional.of(mShellCommandHandler), Optional.of(mSizeCompatUI),
+ Optional.of(mShellCommandHandler), Optional.of(mCompatUI),
+ Optional.of(mDragAndDrop),
mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor,
mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer,
mWakefulnessLifecycle, mSysUiMainExecutor);
@@ -133,8 +136,8 @@
}
@Test
- public void initSizeCompatUI_registersCallbacks() {
- mWMShell.initSizeCompatUi(mSizeCompatUI);
+ public void initCompatUI_registersCallbacks() {
+ mWMShell.initCompatUi(mCompatUI);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
}
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index eaf2694..6744ea8 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -288,8 +288,6 @@
showGlobalActions();
return true;
}
- case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN:
- return toggleSplitScreen();
case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN:
return lockScreen();
case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT:
@@ -369,21 +367,6 @@
mWindowManagerService.showGlobalActions();
}
- private boolean toggleSplitScreen() {
- final long token = Binder.clearCallingIdentity();
- try {
- StatusBarManagerInternal statusBarService = LocalServices.getService(
- StatusBarManagerInternal.class);
- if (statusBarService == null) {
- return false;
- }
- statusBarService.toggleSplitScreen();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return true;
- }
-
private boolean lockScreen() {
mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 9ee0159..0b95fef 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4068,14 +4068,20 @@
}
int operationType;
+ TransportClient transportClient = null;
try {
- operationType = getOperationTypeFromTransport(
- mTransportManager.getTransportClientOrThrow(transport, /* caller */
- "BMS.beginRestoreSession"));
+ transportClient = mTransportManager.getTransportClientOrThrow(
+ transport, /* caller */"BMS.beginRestoreSession");
+ operationType = getOperationTypeFromTransport(transportClient);
} catch (TransportNotAvailableException | TransportNotRegisteredException
| RemoteException e) {
Slog.w(TAG, "Failed to get operation type from transport: " + e);
return null;
+ } finally {
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient,
+ /* caller */"BMS.beginRestoreSession");
+ }
}
synchronized (this) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 51066dd..ff24c6f 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -110,10 +110,6 @@
private static final String BLUETOOTH_PRIVILEGED =
android.Manifest.permission.BLUETOOTH_PRIVILEGED;
- private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
- private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address";
- private static final String SECURE_SETTINGS_BLUETOOTH_NAME = "bluetooth_name";
-
private static final int ACTIVE_LOG_MAX_SIZE = 20;
private static final int CRASH_LOG_MAX_SIZE = 100;
@@ -636,7 +632,7 @@
if (mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
&& Settings.Secure.getIntForUser(mContentResolver,
- SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0, mUserId)
+ Settings.Secure.BLUETOOTH_NAME, 0, mUserId)
== 0) {
// if the valid flag is not set, don't load the address and name
if (DBG) {
@@ -645,9 +641,9 @@
return;
}
mName = Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, mUserId);
+ mContentResolver, Settings.Secure.BLUETOOTH_NAME, mUserId);
mAddress = Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, mUserId);
+ mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS, mUserId);
if (DBG) {
Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
}
@@ -661,30 +657,30 @@
*/
private void storeNameAndAddress(String name, String address) {
if (name != null) {
- Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name,
+ Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_NAME, name,
mUserId);
mName = name;
if (DBG) {
Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME,
+ mContentResolver, Settings.Secure.BLUETOOTH_NAME,
mUserId));
}
}
if (address != null) {
- Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS,
+ Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
address, mUserId);
mAddress = address;
if (DBG) {
Slog.d(TAG,
"Stored Bluetoothaddress: " + Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS,
+ mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
mUserId));
}
}
if ((name != null) && (address != null)) {
- Settings.Secure.putIntForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1,
+ Settings.Secure.putIntForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDR_VALID, 1,
mUserId);
}
}
@@ -1869,6 +1865,10 @@
mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
mEnable = true;
+ if (isBle == 0) {
+ persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+ }
+
// Use service interface to get the exact state
try {
mBluetoothLock.readLock().lock();
@@ -1882,7 +1882,6 @@
} else {
Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
mBluetooth.onLeServiceUp(mContext.getAttributionSource());
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
}
break;
case BluetoothAdapter.STATE_BLE_TURNING_ON:
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index df2ae98..9a371f3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -157,6 +157,7 @@
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.SomeArgs;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
@@ -4209,9 +4210,12 @@
if (r.app != null) {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
- msg.obj = r.app;
- msg.getData().putCharSequence(
- ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = r.app;
+ args.arg2 = r.toString();
+ args.arg3 = r.getComponentName();
+
+ msg.obj = args;
mAm.mHandler.sendMessage(msg);
}
}
@@ -5272,11 +5276,14 @@
}
}
- void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
- mAm.crashApplicationWithType(app.uid, app.getPid(), app.info.packageName, app.userId,
+ void serviceForegroundCrash(ProcessRecord app, String serviceRecord,
+ ComponentName service) {
+ mAm.crashApplicationWithTypeWithExtras(
+ app.uid, app.getPid(), app.info.packageName, app.userId,
"Context.startForegroundService() did not then call Service.startForeground(): "
+ serviceRecord, false /*force*/,
- ForegroundServiceDidNotStartInTimeException.TYPE_ID);
+ ForegroundServiceDidNotStartInTimeException.TYPE_ID,
+ ForegroundServiceDidNotStartInTimeException.createExtrasForService(service));
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5cd66aa..330d2dd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -92,6 +92,7 @@
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -300,6 +301,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -338,6 +340,7 @@
import com.android.internal.os.ByteTransferPipe;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.SomeArgs;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
import com.android.internal.policy.AttributeCache;
@@ -1472,8 +1475,6 @@
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
- static final String SERVICE_RECORD_KEY = "servicerecord";
-
/**
* Flag whether the current user is a "monkey", i.e. whether
* the UI is driven by a UI automation tool.
@@ -1652,8 +1653,12 @@
mServices.serviceForegroundTimeout((ServiceRecord) msg.obj);
} break;
case SERVICE_FOREGROUND_CRASH_MSG: {
- mServices.serviceForegroundCrash((ProcessRecord) msg.obj,
- msg.getData().getCharSequence(SERVICE_RECORD_KEY));
+ SomeArgs args = (SomeArgs) msg.obj;
+ mServices.serviceForegroundCrash(
+ (ProcessRecord) args.arg1,
+ (String) args.arg2,
+ (ComponentName) args.arg3);
+ args.recycle();
} break;
case UPDATE_TIME_ZONE: {
synchronized (mProcLock) {
@@ -3001,6 +3006,14 @@
@Override
public void crashApplicationWithType(int uid, int initialPid, String packageName, int userId,
String message, boolean force, int exceptionTypeId) {
+ crashApplicationWithTypeWithExtras(uid, initialPid, packageName, userId, message,
+ force, exceptionTypeId, null);
+ }
+
+ @Override
+ public void crashApplicationWithTypeWithExtras(int uid, int initialPid, String packageName,
+ int userId, String message, boolean force, int exceptionTypeId,
+ @Nullable Bundle extras) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: crashApplication() from pid="
@@ -3013,7 +3026,7 @@
synchronized(this) {
mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId,
- message, force, exceptionTypeId);
+ message, force, exceptionTypeId, extras);
}
}
@@ -4173,7 +4186,7 @@
didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId,
ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
- evenPersistent, true /* setRemoved */,
+ evenPersistent, true /* setRemoved */, uninstalling,
packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
: ApplicationExitInfo.REASON_USER_REQUESTED,
ApplicationExitInfo.SUBREASON_UNKNOWN,
@@ -7209,6 +7222,7 @@
ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
true /* callerWillRestart */, true /* doit */,
true /* evenPersistent */, false /* setRemoved */,
+ false /* uninstalling */,
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_KILL_UID,
reason != null ? reason : "kill uid");
@@ -7230,6 +7244,7 @@
ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
true /* callerWillRestart */, true /* doit */,
true /* evenPersistent */, false /* setRemoved */,
+ false /* uninstalling */,
ApplicationExitInfo.REASON_PERMISSION_CHANGE,
ApplicationExitInfo.SUBREASON_UNKNOWN,
reason != null ? reason : "kill uid");
@@ -14263,6 +14278,8 @@
private void checkExcessivePowerUsage() {
updateCpuStatsNow();
+ final boolean monitorPhantomProcs = mSystemReady && FeatureFlagUtils.isEnabled(mContext,
+ SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
synchronized (mProcLock) {
final boolean doCpuKills = mLastPowerCheckUptime != 0;
final long curUptime = SystemClock.uptimeMillis();
@@ -14288,9 +14305,11 @@
updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
- // Also check the phantom processes if there is any
- updatePhantomProcessCpuTimeLPr(
- uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+ if (monitorPhantomProcs) {
+ // Also check the phantom processes if there is any
+ updatePhantomProcessCpuTimeLPr(
+ uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+ }
}
});
}
@@ -15091,6 +15110,16 @@
}
@Override
+ public String getSwitchingFromUserMessage() {
+ return mUserController.getSwitchingFromSystemUserMessage();
+ }
+
+ @Override
+ public String getSwitchingToUserMessage() {
+ return mUserController.getSwitchingToSystemUserMessage();
+ }
+
+ @Override
public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
mUserController.setStopUserOnSwitch(value);
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index bcb42bb..0bf0fe2 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -27,6 +27,7 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AnrController;
@@ -40,6 +41,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
@@ -489,7 +491,7 @@
* @param message
*/
void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId,
- String message, boolean force, int exceptionTypeId) {
+ String message, boolean force, int exceptionTypeId, @Nullable Bundle extras) {
ProcessRecord proc = null;
// Figure out which process to kill. We don't trust that initialPid
@@ -521,7 +523,7 @@
return;
}
- proc.scheduleCrashLocked(message, exceptionTypeId);
+ proc.scheduleCrashLocked(message, exceptionTypeId, extras);
if (force) {
// If the app is responsive, the scheduled crash will happen as expected
// and then the delayed summary kill will be a no-op.
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index ad0485b..293b8a9 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
@@ -77,6 +78,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -1783,6 +1785,8 @@
}
void updateCpuStatsNow() {
+ final boolean monitorPhantomProcs = mService.mSystemReady && FeatureFlagUtils.isEnabled(
+ mService.mContext, SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
synchronized (mProcessCpuTracker) {
mProcessCpuMutexFree.set(false);
final long now = SystemClock.uptimeMillis();
@@ -1821,7 +1825,7 @@
}
}
- if (haveNewCpuStats) {
+ if (monitorPhantomProcs && haveNewCpuStats) {
mService.mPhantomProcessList.updateProcessCpuStatesLocked(mProcessCpuTracker);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 487101b..2da4107 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -611,7 +611,7 @@
Slog.w(TAG, "Can't deliver broadcast to " + app.processName
+ " (pid " + app.getPid() + "). Crashing it.");
app.scheduleCrashLocked("can't deliver broadcast",
- CannotDeliverBroadcastException.TYPE_ID);
+ CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
}
throw ex;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index ff480d1..7673123 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1633,7 +1633,7 @@
int schedGroup;
int procState;
int cachedAdjSeq;
- int capability = 0;
+ int capability = cycleReEval ? app.mState.getCurCapability() : 0;
boolean foregroundActivities = false;
boolean hasVisibleActivities = false;
@@ -2018,10 +2018,6 @@
}
if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
- if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
- continue;
- }
-
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
capability |= cstate.getCurCapability();
}
@@ -2042,6 +2038,10 @@
}
}
+ if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
+ continue;
+ }
+
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index b07684c..2ec1aed 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -18,6 +18,7 @@
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -28,6 +29,7 @@
import android.os.Handler;
import android.os.Process;
import android.os.StrictMode;
+import android.util.FeatureFlagUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -419,6 +421,10 @@
* order of the oom adjs of their parent process.
*/
void trimPhantomProcessesIfNecessary() {
+ if (!mService.mSystemReady || !FeatureFlagUtils.isEnabled(mService.mContext,
+ SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS)) {
+ return;
+ }
synchronized (mService.mProcLock) {
synchronized (mLock) {
mTrimPhantomProcessScheduled = false;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b77270f..1e66ed4 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -374,6 +374,16 @@
private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
/**
+ * Native heap allocations in AppZygote process and its descendants will now have a
+ * non-zero tag in the most significant byte.
+ * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+ * Pointers</a>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+ private static final long NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE = 207557677;
+
+ /**
* Enable asynchronous (ASYNC) memory tag checking in this process. This
* flag will only have an effect on hardware supporting the ARM Memory
* Tagging Extension (MTE).
@@ -1738,6 +1748,16 @@
return level;
}
+ private int decideTaggingLevelForAppZygote(ProcessRecord app) {
+ int level = decideTaggingLevel(app);
+ // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature.
+ if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE, app.info)
+ && level == Zygote.MEMORY_TAG_LEVEL_TBI) {
+ level = Zygote.MEMORY_TAG_LEVEL_NONE;
+ }
+ return level;
+ }
+
private int decideGwpAsanLevel(ProcessRecord app) {
// Look at the process attribute first.
if (app.processInfo != null
@@ -2238,7 +2258,8 @@
// not the calling one.
appInfo.packageName = app.getHostingRecord().getDefiningPackageName();
appInfo.uid = uid;
- appZygote = new AppZygote(appInfo, uid, firstUid, lastUid);
+ int runtimeFlags = decideTaggingLevelForAppZygote(app);
+ appZygote = new AppZygote(appInfo, uid, firstUid, lastUid, runtimeFlags);
mAppZygotes.put(app.info.processName, uid, appZygote);
zygoteProcessList = new ArrayList<ProcessRecord>();
mAppZygoteProcesses.put(appZygote, zygoteProcessList);
@@ -2750,8 +2771,8 @@
int reasonCode, int subReason, String reason) {
return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
- false /* evenPersistent */, false /* setRemoved */, reasonCode,
- subReason, reason);
+ false /* evenPersistent */, false /* setRemoved */, false /* uninstalling */,
+ reasonCode, subReason, reason);
}
@GuardedBy("mService")
@@ -2784,9 +2805,10 @@
@GuardedBy({"mService", "mProcLock"})
boolean killPackageProcessesLSP(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
- boolean doit, boolean evenPersistent, boolean setRemoved, int reasonCode,
- int subReason, String reason) {
- ArrayList<ProcessRecord> procs = new ArrayList<>();
+ boolean doit, boolean evenPersistent, boolean setRemoved, boolean uninstalling,
+ int reasonCode, int subReason, String reason) {
+ final PackageManagerInternal pm = mService.getPackageManagerInternal();
+ final ArrayList<Pair<ProcessRecord, Boolean>> procs = new ArrayList<>();
// Remove all processes this package may have touched: all with the
// same UID (except for the system or root user), and all whose name
@@ -2803,7 +2825,18 @@
}
if (app.isRemoved()) {
if (doit) {
- procs.add(app);
+ boolean shouldAllowRestart = false;
+ if (!uninstalling && packageName != null) {
+ // This package has a dependency on the given package being stopped,
+ // while it's not being frozen nor uninstalled, allow to restart it.
+ shouldAllowRestart = !app.getPkgList().containsKey(packageName)
+ && app.getPkgDeps() != null
+ && app.getPkgDeps().contains(packageName)
+ && app.info != null
+ && !pm.isPackageFrozen(app.info.packageName, app.uid,
+ app.userId);
+ }
+ procs.add(new Pair<>(app, shouldAllowRestart));
}
continue;
}
@@ -2818,6 +2851,8 @@
continue;
}
+ boolean shouldAllowRestart = false;
+
// If no package is specified, we call all processes under the
// give user id.
if (packageName == null) {
@@ -2839,9 +2874,16 @@
if (userId != UserHandle.USER_ALL && app.userId != userId) {
continue;
}
- if (!app.getPkgList().containsKey(packageName) && !isDep) {
+ final boolean isInPkgList = app.getPkgList().containsKey(packageName);
+ if (!isInPkgList && !isDep) {
continue;
}
+ if (!isInPkgList && isDep && !uninstalling && app.info != null
+ && !pm.isPackageFrozen(app.info.packageName, app.uid, app.userId)) {
+ // This package has a dependency on the given package being stopped,
+ // while it's not being frozen nor uninstalled, allow to restart it.
+ shouldAllowRestart = true;
+ }
}
// Process has passed all conditions, kill it!
@@ -2851,14 +2893,15 @@
if (setRemoved) {
app.setRemoved(true);
}
- procs.add(app);
+ procs.add(new Pair<>(app, shouldAllowRestart));
}
}
int N = procs.size();
for (int i=0; i<N; i++) {
- removeProcessLocked(procs.get(i), callerWillRestart, allowRestart,
- reasonCode, subReason, reason);
+ final Pair<ProcessRecord, Boolean> proc = procs.get(i);
+ removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
+ reasonCode, subReason, reason);
}
killAppZygotesLocked(packageName, appId, userId, false /* force */);
mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 14ba716..b9d7917 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -31,6 +31,7 @@
import android.content.pm.VersionedPackage;
import android.content.res.CompatibilityInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -958,7 +959,7 @@
* of its subclasses.
*/
@GuardedBy("mService")
- void scheduleCrashLocked(String message, int exceptionTypeId) {
+ void scheduleCrashLocked(String message, int exceptionTypeId, @Nullable Bundle extras) {
// Checking killedbyAm should keep it from showing the crash dialog if the process
// was already dead for a good / normal reason.
if (!mKilledByAm) {
@@ -969,7 +970,7 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- mThread.scheduleCrash(message, exceptionTypeId);
+ mThread.scheduleCrash(message, exceptionTypeId, extras);
} catch (RemoteException e) {
// If it's already dead our work is done. If it's wedged just kill it.
// We won't get the crash dialog or the error reporting.
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 429696f..b28b1a6 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1792,7 +1792,8 @@
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
- getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage());
+ getSwitchingFromSystemUserMessageUnchecked(),
+ getSwitchingToSystemUserMessageUnchecked());
}
private void dispatchForegroundProfileChanged(@UserIdInt int userId) {
@@ -2564,18 +2565,40 @@
}
}
- private String getSwitchingFromSystemUserMessage() {
+ // Called by AMS, must check permission
+ String getSwitchingFromSystemUserMessage() {
+ checkHasManageUsersPermission("getSwitchingFromSystemUserMessage()");
+
+ return getSwitchingFromSystemUserMessageUnchecked();
+ }
+
+ // Called by AMS, must check permission
+ String getSwitchingToSystemUserMessage() {
+ checkHasManageUsersPermission("getSwitchingToSystemUserMessage()");
+
+ return getSwitchingToSystemUserMessageUnchecked();
+ }
+
+ private String getSwitchingFromSystemUserMessageUnchecked() {
synchronized (mLock) {
return mSwitchingFromSystemUserMessage;
}
}
- private String getSwitchingToSystemUserMessage() {
+ private String getSwitchingToSystemUserMessageUnchecked() {
synchronized (mLock) {
return mSwitchingToSystemUserMessage;
}
}
+ private void checkHasManageUsersPermission(String operation) {
+ if (mInjector.checkCallingPermission(
+ android.Manifest.permission.MANAGE_USERS) == PackageManager.PERMISSION_DENIED) {
+ throw new SecurityException(
+ "You need MANAGE_USERS permission to call " + operation);
+ }
+ }
+
void dumpDebug(ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
long token = proto.start(fieldId);
@@ -2648,6 +2671,12 @@
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
pw.println(" mInitialized:" + mInitialized);
+ if (mSwitchingFromSystemUserMessage != null) {
+ pw.println(" mSwitchingFromSystemUserMessage: " + mSwitchingFromSystemUserMessage);
+ }
+ if (mSwitchingToSystemUserMessage != null) {
+ pw.println(" mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
+ }
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 64b9bd9..6d29c37 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1314,6 +1314,7 @@
event.getAttributionFlags(), event.getAttributionChainId());
}
+ events = isRunning ? mInProgressEvents : mPausedInProgressEvents;
InProgressStartOpEvent newEvent = events.get(binders.get(i));
if (newEvent != null) {
newEvent.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 5ecdfe4..f053e94 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -328,7 +328,7 @@
}
boolean isBtScoRequested = isBluetoothScoRequested();
- if (isBtScoRequested && !wasBtScoRequested) {
+ if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
+ pid);
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 6a26bea..b47ea4f 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -551,9 +551,9 @@
logd("canBeSpatialized false due to usage:" + attributes.getUsage());
return false;
}
- AudioDeviceAttributes[] devices =
- // going through adapter to take advantage of routing cache
- (AudioDeviceAttributes[]) mASA.getDevicesForAttributes(attributes).toArray();
+ AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
+ // going through adapter to take advantage of routing cache
+ mASA.getDevicesForAttributes(attributes).toArray(devices);
final boolean able = AudioSystem.canBeSpatialized(attributes, format, devices);
logd("canBeSpatialized returning " + able);
return able;
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index f42870b..758cf7a 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1036,7 +1036,8 @@
promptInfo.setAuthenticators(authenticators);
return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors,
- userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */);
+ userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */,
+ getContext());
}
/**
@@ -1375,7 +1376,8 @@
try {
final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager,
mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo,
- opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists());
+ opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(),
+ getContext());
final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();
@@ -1383,8 +1385,11 @@
+ "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
+ " requestId: " + requestId + " promptInfo.isIgnoreEnrollmentState: "
+ promptInfo.isIgnoreEnrollmentState());
-
- if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
+ // BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED is added so that BiometricPrompt can
+ // be shown for this case.
+ if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS
+ || preAuthStatus.second
+ == BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED) {
// If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
// CREDENTIAL is requested and available, set the bundle to only request
// CREDENTIAL.
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index a5a3542..05c3f68 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -26,6 +26,8 @@
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
+import android.content.Context;
+import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.PromptInfo;
@@ -59,6 +61,7 @@
static final int CREDENTIAL_NOT_ENROLLED = 9;
static final int BIOMETRIC_LOCKOUT_TIMED = 10;
static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
+ static final int BIOMETRIC_SENSOR_PRIVACY_ENABLED = 12;
@IntDef({AUTHENTICATOR_OK,
BIOMETRIC_NO_HARDWARE,
BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
@@ -69,7 +72,8 @@
BIOMETRIC_NOT_ENABLED_FOR_APPS,
CREDENTIAL_NOT_ENROLLED,
BIOMETRIC_LOCKOUT_TIMED,
- BIOMETRIC_LOCKOUT_PERMANENT})
+ BIOMETRIC_LOCKOUT_PERMANENT,
+ BIOMETRIC_SENSOR_PRIVACY_ENABLED})
@Retention(RetentionPolicy.SOURCE)
@interface AuthenticatorStatus {}
@@ -84,13 +88,15 @@
final boolean credentialAvailable;
final boolean confirmationRequested;
final boolean ignoreEnrollmentState;
+ final int userId;
+ final Context context;
static PreAuthInfo create(ITrustManager trustManager,
DevicePolicyManager devicePolicyManager,
BiometricService.SettingObserver settingObserver,
List<BiometricSensor> sensors,
int userId, PromptInfo promptInfo, String opPackageName,
- boolean checkDevicePolicyManager)
+ boolean checkDevicePolicyManager, Context context)
throws RemoteException {
final boolean confirmationRequested = promptInfo.isConfirmationRequested();
@@ -116,14 +122,22 @@
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
checkDevicePolicyManager, requestedStrength,
promptInfo.getAllowedSensorIds(),
- promptInfo.isIgnoreEnrollmentState());
+ promptInfo.isIgnoreEnrollmentState(),
+ context);
Slog.d(TAG, "Package: " + opPackageName
+ " Sensor ID: " + sensor.id
+ " Modality: " + sensor.modality
+ " Status: " + status);
- if (status == AUTHENTICATOR_OK) {
+ // A sensor with privacy enabled will still be eligible to
+ // authenticate with biometric prompt. This is so the framework can display
+ // a sensor privacy error message to users after briefly showing the
+ // Biometric Prompt.
+ //
+ // Note: if only a certain sensor is required and the privacy is enabled,
+ // canAuthenticate() will return false.
+ if (status == AUTHENTICATOR_OK || status == BIOMETRIC_SENSOR_PRIVACY_ENABLED) {
eligibleSensors.add(sensor);
} else {
ineligibleSensors.add(new Pair<>(sensor, status));
@@ -133,7 +147,7 @@
return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
- promptInfo.isIgnoreEnrollmentState());
+ promptInfo.isIgnoreEnrollmentState(), userId, context);
}
/**
@@ -149,7 +163,7 @@
BiometricSensor sensor, int userId, String opPackageName,
boolean checkDevicePolicyManager, int requestedStrength,
@NonNull List<Integer> requestedSensorIds,
- boolean ignoreEnrollmentState) {
+ boolean ignoreEnrollmentState, Context context) {
if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
return BIOMETRIC_NO_HARDWARE;
@@ -175,6 +189,16 @@
&& !ignoreEnrollmentState) {
return BIOMETRIC_NOT_ENROLLED;
}
+ final SensorPrivacyManager sensorPrivacyManager = context
+ .getSystemService(SensorPrivacyManager.class);
+
+ if (sensorPrivacyManager != null && sensor.modality == TYPE_FACE) {
+ if (sensorPrivacyManager
+ .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId)) {
+ return BIOMETRIC_SENSOR_PRIVACY_ENABLED;
+ }
+ }
+
final @LockoutTracker.LockoutMode int lockoutMode =
sensor.impl.getLockoutModeForUser(userId);
@@ -243,7 +267,8 @@
private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
boolean credentialRequested, List<BiometricSensor> eligibleSensors,
List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
- boolean confirmationRequested, boolean ignoreEnrollmentState) {
+ boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
+ Context context) {
mBiometricRequested = biometricRequested;
mBiometricStrengthRequested = biometricStrengthRequested;
this.credentialRequested = credentialRequested;
@@ -253,6 +278,8 @@
this.credentialAvailable = credentialAvailable;
this.confirmationRequested = confirmationRequested;
this.ignoreEnrollmentState = ignoreEnrollmentState;
+ this.userId = userId;
+ this.context = context;
}
private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
@@ -280,15 +307,35 @@
private Pair<Integer, Integer> getInternalStatus() {
@AuthenticatorStatus final int status;
@BiometricAuthenticator.Modality int modality = TYPE_NONE;
+
+ final SensorPrivacyManager sensorPrivacyManager = context
+ .getSystemService(SensorPrivacyManager.class);
+
+ boolean cameraPrivacyEnabled = false;
+ if (sensorPrivacyManager != null) {
+ cameraPrivacyEnabled = sensorPrivacyManager
+ .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId);
+ }
+
if (mBiometricRequested && credentialRequested) {
if (credentialAvailable || !eligibleSensors.isEmpty()) {
- status = AUTHENTICATOR_OK;
- if (credentialAvailable) {
- modality |= TYPE_CREDENTIAL;
- }
for (BiometricSensor sensor : eligibleSensors) {
modality |= sensor.modality;
}
+
+ if (credentialAvailable) {
+ modality |= TYPE_CREDENTIAL;
+ status = AUTHENTICATOR_OK;
+ } else if (modality == TYPE_FACE && cameraPrivacyEnabled) {
+ // If the only modality requested is face, credential is unavailable,
+ // and the face sensor privacy is enabled then return
+ // BIOMETRIC_SENSOR_PRIVACY_ENABLED.
+ //
+ // Note: This sensor will still be eligible for calls to authenticate.
+ status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
+ } else {
+ status = AUTHENTICATOR_OK;
+ }
} else {
// Pick the first sensor error if it exists
if (!ineligibleSensors.isEmpty()) {
@@ -302,10 +349,18 @@
}
} else if (mBiometricRequested) {
if (!eligibleSensors.isEmpty()) {
- status = AUTHENTICATOR_OK;
- for (BiometricSensor sensor : eligibleSensors) {
- modality |= sensor.modality;
- }
+ for (BiometricSensor sensor : eligibleSensors) {
+ modality |= sensor.modality;
+ }
+ if (modality == TYPE_FACE && cameraPrivacyEnabled) {
+ // If the only modality requested is face and the privacy is enabled
+ // then return BIOMETRIC_SENSOR_PRIVACY_ENABLED.
+ //
+ // Note: This sensor will still be eligible for calls to authenticate.
+ status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
+ } else {
+ status = AUTHENTICATOR_OK;
+ }
} else {
// Pick the first sensor error if it exists
if (!ineligibleSensors.isEmpty()) {
@@ -326,9 +381,9 @@
Slog.e(TAG, "No authenticators requested");
status = BIOMETRIC_NO_HARDWARE;
}
-
Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality
+ " AuthenticatorStatus: " + status);
+
return new Pair<>(modality, status);
}
@@ -362,6 +417,7 @@
case CREDENTIAL_NOT_ENROLLED:
case BIOMETRIC_LOCKOUT_TIMED:
case BIOMETRIC_LOCKOUT_PERMANENT:
+ case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
break;
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 4f7c6b0..0e2582c 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -33,6 +33,7 @@
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENABLED_FOR_APPS;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
+import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_SENSOR_PRIVACY_ENABLED;
import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
import android.annotation.NonNull;
@@ -278,6 +279,9 @@
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
break;
+ case BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED:
+ biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+ break;
default:
Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
@@ -337,7 +341,8 @@
case BIOMETRIC_LOCKOUT_PERMANENT:
return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-
+ case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
+ return BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED;
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
case BIOMETRIC_HARDWARE_NOT_DETECTED:
case BIOMETRIC_NOT_ENABLED_FOR_APPS:
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 61b8ded..358263d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -251,7 +251,7 @@
"Successful background authentication!");
}
- mAlreadyDone = true;
+ markAlreadyDone();
if (mTaskStackListener != null) {
mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
@@ -327,7 +327,7 @@
final @LockoutTracker.LockoutMode int lockoutMode =
handleFailedAttempt(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
- mAlreadyDone = true;
+ markAlreadyDone();
}
final CoexCoordinator coordinator = CoexCoordinator.getInstance();
@@ -503,10 +503,14 @@
protected int getShowOverlayReason() {
if (isKeyguard()) {
return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
- } else if (isSettings()) {
- return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
} else if (isBiometricPrompt()) {
+ // BP reason always takes precedent over settings, since callers from within
+ // settings can always invoke BP.
return BiometricOverlayConstants.REASON_AUTH_BP;
+ } else if (isSettings()) {
+ // This is pretty much only for FingerprintManager#authenticate usage from
+ // FingerprintSettings.
+ return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
} else {
return BiometricOverlayConstants.REASON_AUTH_OTHER;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 9764a16..b73e911 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -114,7 +114,7 @@
// Currently only used for authentication client. The cookie generated by BiometricService
// is never 0.
private final int mCookie;
- boolean mAlreadyDone;
+ private boolean mAlreadyDone = false;
// Use an empty callback by default since delayed operations can receive events
// before they are started and cause NPE in subclasses that access this field directly.
@@ -202,11 +202,9 @@
return callback;
}
- public boolean isAlreadyDone() {
- return mAlreadyDone;
- }
-
- public void destroy() {
+ /** Signals this operation has completed its lifecycle and should no longer be used. */
+ void destroy() {
+ mAlreadyDone = true;
if (mToken != null) {
try {
mToken.unlinkToDeath(this, 0);
@@ -218,6 +216,20 @@
}
}
+ /**
+ * Call while the operation is still active, but nearly done, to prevent any action
+ * upon client death (only needed for authentication clients).
+ */
+ void markAlreadyDone() {
+ Slog.d(TAG, "marking operation as done: " + this);
+ mAlreadyDone = true;
+ }
+
+ /** If this operation has been marked as completely done (or cancelled). */
+ public boolean isAlreadyDone() {
+ return mAlreadyDone;
+ }
+
@Override
public void binderDied() {
binderDiedInternal(true /* clearListener */);
@@ -225,10 +237,9 @@
// TODO(b/157790417): Move this to the scheduler
void binderDiedInternal(boolean clearListener) {
- Slog.e(TAG, "Binder died, owner: " + getOwnerString()
- + ", operation: " + this.getClass().getName());
+ Slog.e(TAG, "Binder died, operation: " + this);
- if (isAlreadyDone()) {
+ if (mAlreadyDone) {
Slog.w(TAG, "Binder died but client is finished, ignoring");
return;
}
@@ -299,7 +310,7 @@
@Override
public String toString() {
return "{[" + mSequentialId + "] "
- + this.getClass().getSimpleName()
+ + this.getClass().getName()
+ ", proto=" + getProtoEnum()
+ ", owner=" + getOwnerString()
+ ", cookie=" + getCookie()
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 361ec40..a358bc2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -605,6 +605,9 @@
if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
// We can set it to null immediately, since the HAL was never notified to start.
+ if (mCurrentOperation != null) {
+ mCurrentOperation.mClientMonitor.destroy();
+ }
mCurrentOperation = null;
startNextOperationIfIdle();
return;
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 97d791b..4131ae1 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
@@ -21,6 +21,7 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
@@ -56,6 +57,7 @@
@NonNull private final LockoutCache mLockoutCache;
@Nullable private final NotificationManager mNotificationManager;
@Nullable private ICancellationSignal mCancellationSignal;
+ @Nullable private SensorPrivacyManager mSensorPrivacyManager;
private final int[] mBiometricPromptIgnoreList;
private final int[] mBiometricPromptIgnoreListVendor;
@@ -81,6 +83,7 @@
mUsageStats = usageStats;
mLockoutCache = lockoutCache;
mNotificationManager = context.getSystemService(NotificationManager.class);
+ mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
final Resources resources = getContext().getResources();
mBiometricPromptIgnoreList = resources.getIntArray(
@@ -108,7 +111,16 @@
@Override
protected void startHalOperation() {
try {
- mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
+ if (mSensorPrivacyManager != null
+ && mSensorPrivacyManager
+ .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
+ getTargetUserId())) {
+ onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ } else {
+ mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 2ef0911..2158dfe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.ISession;
@@ -41,6 +43,7 @@
private final boolean mIsStrongBiometric;
@Nullable private ICancellationSignal mCancellationSignal;
+ @Nullable private SensorPrivacyManager mSensorPrivacyManager;
public FaceDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, long requestId,
@@ -51,6 +54,7 @@
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
+ mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
}
@Override
@@ -73,6 +77,14 @@
@Override
protected void startHalOperation() {
+ if (mSensorPrivacyManager != null
+ && mSensorPrivacyManager
+ .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, getTargetUserId())) {
+ onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ return;
+ }
+
try {
mCancellationSignal = getFreshDaemon().detectInteraction();
} catch (RemoteException e) {
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 40f2801..7548d28 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
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
@@ -55,6 +56,7 @@
private final int[] mKeyguardIgnoreListVendor;
private int mLastAcquire;
+ private SensorPrivacyManager mSensorPrivacyManager;
FaceAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
@@ -71,6 +73,7 @@
isKeyguardBypassEnabled);
setRequestId(requestId);
mUsageStats = usageStats;
+ mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
final Resources resources = getContext().getResources();
mBiometricPromptIgnoreList = resources.getIntArray(
@@ -97,6 +100,15 @@
@Override
protected void startHalOperation() {
+
+ if (mSensorPrivacyManager != null
+ && mSensorPrivacyManager
+ .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, getTargetUserId())) {
+ onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ return;
+ }
+
try {
getFreshDaemon().authenticate(mOperationId);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 5ce72c2..3120dc5 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -25,7 +25,6 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.compat.CompatChanges;
-import android.app.TaskStackListener;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
@@ -35,6 +34,7 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.hardware.CameraSessionStats;
import android.hardware.CameraStreamStats;
@@ -84,7 +84,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -309,8 +308,6 @@
private final DisplayWindowListener mDisplayWindowListener = new DisplayWindowListener();
- private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
-
public static final class TaskInfo {
public int frontTaskId;
public boolean isResizeable;
@@ -320,54 +317,6 @@
public int userId;
}
- private final class TaskStateHandler extends TaskStackListener {
- private final Object mMapLock = new Object();
-
- // maps the package name to its corresponding current top level task id
- @GuardedBy("mMapLock")
- private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>();
-
- @Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
- synchronized (mMapLock) {
- TaskInfo info = new TaskInfo();
- info.frontTaskId = taskInfo.taskId;
- info.isResizeable =
- (taskInfo.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
- info.displayId = taskInfo.displayId;
- info.userId = taskInfo.userId;
- info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
- taskInfo.topActivityInfo.screenOrientation);
- info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
- taskInfo.topActivityInfo.screenOrientation);
- mTaskInfoMap.put(taskInfo.topActivityInfo.packageName, info);
- }
- }
-
- @Override
- public void onTaskRemoved(int taskId) {
- synchronized (mMapLock) {
- for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){
- if (entry.getValue().frontTaskId == taskId) {
- mTaskInfoMap.remove(entry.getKey());
- break;
- }
- }
- }
- }
-
- public @Nullable TaskInfo getFrontTaskInfo(String packageName) {
- synchronized (mMapLock) {
- if (mTaskInfoMap.containsKey(packageName)) {
- return mTaskInfoMap.get(packageName);
- }
- }
-
- Log.e(TAG, "Top task with package name: " + packageName + " not found!");
- return null;
- }
- }
-
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -490,18 +439,53 @@
private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
@Override
- public int getRotateAndCropOverride(String packageName, int lensFacing) {
+ public int getRotateAndCropOverride(String packageName, int lensFacing, int userId) {
if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
" camera service UID!");
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
+ TaskInfo taskInfo = null;
+ ParceledListSlice<ActivityManager.RecentTaskInfo> recentTasks = null;
+
+ try {
+ recentTasks = ActivityTaskManager.getService().getRecentTasks(/*maxNum*/1,
+ /*flags*/ 0, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query recent tasks!");
+ return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+ }
+
+ if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) {
+ ActivityManager.RecentTaskInfo task = recentTasks.getList().get(0);
+ if (packageName.equals(task.topActivityInfo.packageName)) {
+ taskInfo = new TaskInfo();
+ taskInfo.frontTaskId = task.taskId;
+ taskInfo.isResizeable =
+ (task.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
+ taskInfo.displayId = task.displayId;
+ taskInfo.userId = task.userId;
+ taskInfo.isFixedOrientationLandscape =
+ ActivityInfo.isFixedOrientationLandscape(
+ task.topActivityInfo.screenOrientation);
+ taskInfo.isFixedOrientationPortrait =
+ ActivityInfo.isFixedOrientationPortrait(
+ task.topActivityInfo.screenOrientation);
+ } else {
+ Log.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName
+ + " doesn't match with camera client package name: " + packageName);
+ return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+ }
+ } else {
+ Log.e(TAG, "Recent task list is empty!");
+ return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+ }
+
// TODO: Modify the sensor orientation in camera characteristics along with any 3A
// regions in capture requests/results to account for thea physical rotation. The
// former is somewhat tricky as it assumes that camera clients always check for the
// current value by retrieving the camera characteristics from the camera device.
- TaskInfo taskInfo = mTaskStackListener.getFrontTaskInfo(packageName);
if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName,
UserHandle.getUserHandleForUid(taskInfo.userId)))) {
@@ -673,12 +657,6 @@
CameraStatsJobService.schedule(mContext);
try {
- ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register task stack listener!");
- }
-
- try {
int[] displayIds = WindowManagerGlobal.getWindowManagerService()
.registerDisplayWindowListener(mDisplayWindowListener);
for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1acbde9..3762cca 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -17,6 +17,8 @@
package com.android.server.connectivity;
import static android.Manifest.permission.BIND_VPN_SERVICE;
+import static android.Manifest.permission.CONTROL_VPN;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -891,6 +893,7 @@
* - oldPackage null, newPackage non-null: ConfirmDialog calling prepareVpn().
* - oldPackage null, newPackage=LEGACY_VPN: Used internally to disconnect
* and revoke any current app VPN and re-prepare legacy vpn.
+ * - oldPackage null, newPackage null: always returns true for backward compatibility.
*
* TODO: Rename the variables - or split this method into two - and end this confusion.
* TODO: b/29032008 Migrate code from prepare(oldPackage=non-null, newPackage=LEGACY_VPN)
@@ -904,6 +907,18 @@
*/
public synchronized boolean prepare(
String oldPackage, String newPackage, @VpnManager.VpnType int vpnType) {
+ // Except for Settings and VpnDialogs, the caller should be matched one of oldPackage or
+ // newPackage. Otherwise, non VPN owner might get the VPN always-on status of the VPN owner.
+ // See b/191382886.
+ if (mContext.checkCallingOrSelfPermission(CONTROL_VPN) != PERMISSION_GRANTED) {
+ if (oldPackage != null) {
+ verifyCallingUidAndPackage(oldPackage);
+ }
+ if (newPackage != null) {
+ verifyCallingUidAndPackage(newPackage);
+ }
+ }
+
if (oldPackage != null) {
// Stop an existing always-on VPN from being dethroned by other apps.
if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
@@ -1803,14 +1818,13 @@
}
private void enforceControlPermission() {
- mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
+ mContext.enforceCallingPermission(CONTROL_VPN, "Unauthorized Caller");
}
private void enforceControlPermissionOrInternalCaller() {
// Require the caller to be either an application with CONTROL_VPN permission or a process
// in the system server.
- mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
- "Unauthorized Caller");
+ mContext.enforceCallingOrSelfPermission(CONTROL_VPN, "Unauthorized Caller");
}
private void enforceSettingsPermission() {
@@ -3115,8 +3129,9 @@
}
private void verifyCallingUidAndPackage(String packageName) {
- if (getAppUid(packageName, mUserId) != Binder.getCallingUid()) {
- throw new SecurityException("Mismatched package and UID");
+ final int callingUid = Binder.getCallingUid();
+ if (getAppUid(packageName, mUserId) != callingUid) {
+ throw new SecurityException(packageName + " does not belong to uid " + callingUid);
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 792feea..806a5dd 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -31,7 +31,6 @@
import android.content.Context;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
-import android.hardware.devicestate.DeviceStateManagerInternal;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
@@ -162,7 +161,6 @@
@Override
public void onStart() {
publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
- publishLocalService(DeviceStateManagerInternal.class, new LocalService());
}
@VisibleForTesting
@@ -242,6 +240,13 @@
}
/** Returns the list of currently supported device state identifiers. */
+ private int[] getSupportedStateIdentifiers() {
+ synchronized (mLock) {
+ return getSupportedStateIdentifiersLocked();
+ }
+ }
+
+ /** Returns the list of currently supported device state identifiers. */
private int[] getSupportedStateIdentifiersLocked() {
int[] supportedStates = new int[mDeviceStates.size()];
for (int i = 0; i < supportedStates.length; i++) {
@@ -843,14 +848,4 @@
}
}
}
-
- /** Implementation of {@link DeviceStateManagerInternal} published as a local service. */
- private final class LocalService extends DeviceStateManagerInternal {
- @Override
- public int[] getSupportedStateIdentifiers() {
- synchronized (mLock) {
- return getSupportedStateIdentifiersLocked();
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index fadaf10..b394d6b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -53,7 +53,6 @@
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
-import android.hardware.devicestate.DeviceStateManagerInternal;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
@@ -64,6 +63,8 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
+import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -132,7 +133,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
-import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
@@ -212,7 +212,6 @@
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
private IMediaProjectionManager mProjectionService;
- private DeviceStateManagerInternal mDeviceStateManager;
private int[] mUserDisabledHdrTypes = {};
private boolean mAreUserDisabledHdrTypesAllowed = true;
@@ -560,9 +559,10 @@
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
- mDeviceStateManager = LocalServices.getService(DeviceStateManagerInternal.class);
- mContext.getSystemService(DeviceStateManager.class).registerCallback(
- new HandlerExecutor(mHandler), new DeviceStateListener());
+ DeviceStateManager deviceStateManager =
+ mContext.getSystemService(DeviceStateManager.class);
+ deviceStateManager.registerCallback(new HandlerExecutor(mHandler),
+ new DeviceStateListener());
scheduleTraversalLocked(false);
}
@@ -651,9 +651,6 @@
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
- // Do not let constrain be overwritten by override from WindowManager.
- info.shouldConstrainMetricsForLauncher =
- display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher;
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
handleLogicalDisplayChangedLocked(display);
scheduleTraversalLocked(false);
@@ -1783,21 +1780,6 @@
}
}
- void setShouldConstrainMetricsForLauncher(boolean constrain) {
- // Apply constrain for every display.
- synchronized (mSyncRoot) {
- int[] displayIds = mLogicalDisplayMapper.getDisplayIdsLocked(Process.myUid());
- for (int i : displayIds) {
- final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(i);
- if (display == null) {
- return;
- }
- display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher = constrain;
- setDisplayInfoOverrideFromWindowManagerInternal(i, display.getDisplayInfoLocked());
- }
- }
- }
-
private void clearViewportsLocked() {
mViewports.clear();
}
@@ -3292,53 +3274,6 @@
}
@Override
- public Set<DisplayInfo> getPossibleDisplayInfo(int displayId) {
- synchronized (mSyncRoot) {
- // Retrieve the group associated with this display id.
- final int displayGroupId =
- mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(displayId);
- if (displayGroupId == Display.INVALID_DISPLAY_GROUP) {
- Slog.w(TAG,
- "Can't get possible display info since display group for " + displayId
- + " does not exist");
- return new ArraySet<>();
- }
-
- // Assume any display in this group can be swapped out for the given display id.
- Set<DisplayInfo> possibleInfo = new ArraySet<>();
- final DisplayGroup group = mLogicalDisplayMapper.getDisplayGroupLocked(
- displayGroupId);
- for (int i = 0; i < group.getSizeLocked(); i++) {
- final int id = group.getIdLocked(i);
- final LogicalDisplay logical = mLogicalDisplayMapper.getDisplayLocked(id);
- if (logical == null) {
- Slog.w(TAG,
- "Can't get possible display info since logical display for "
- + "display id " + id + " does not exist, as part of group "
- + displayGroupId);
- } else {
- possibleInfo.add(logical.getDisplayInfoLocked());
- }
- }
-
- // For the supported device states, retrieve the DisplayInfos for the logical
- // display layout.
- if (mDeviceStateManager == null) {
- Slog.w(TAG, "Can't get supported states since DeviceStateManager not ready");
- } else {
- final int[] supportedStates =
- mDeviceStateManager.getSupportedStateIdentifiers();
- for (int state : supportedStates) {
- possibleInfo.addAll(
- mLogicalDisplayMapper.getDisplayInfoForStateLocked(state, displayId,
- displayGroupId));
- }
- }
- return possibleInfo;
- }
- }
-
- @Override
public Point getDisplayPosition(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 9412c93..158c8f0 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -58,8 +58,6 @@
return setDisplayModeDirectorLoggingEnabled(false);
case "dwb-set-cct":
return setAmbientColorTemperatureOverride();
- case "constrain-launcher-metrics":
- return setConstrainLauncherMetrics();
default:
return handleDefaultCommands(cmd);
}
@@ -90,9 +88,6 @@
pw.println(" Disable display mode director logging.");
pw.println(" dwb-set-cct CCT");
pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable).");
- pw.println(" constrain-launcher-metrics [true|false]");
- pw.println(" Sets if Display#getRealSize and getRealMetrics should be constrained for ");
- pw.println(" Launcher.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -155,15 +150,4 @@
mService.setAmbientColorTemperatureOverride(cct);
return 0;
}
-
- private int setConstrainLauncherMetrics() {
- String constrainText = getNextArg();
- if (constrainText == null) {
- getErrPrintWriter().println("Error: no value specified");
- return 1;
- }
- boolean constrain = Boolean.parseBoolean(constrainText);
- mService.setShouldConstrainMetricsForLauncher(constrain);
- return 0;
- }
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 0a22f2f..a4f7c85 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -41,7 +41,6 @@
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -618,10 +617,11 @@
mAppRequestObserver.dumpLocked(pw);
mBrightnessObserver.dumpLocked(pw);
mUdfpsObserver.dumpLocked(pw);
- mSensorObserver.dumpLocked(pw);
mHbmObserver.dumpLocked(pw);
mSkinThermalStatusObserver.dumpLocked(pw);
}
+
+ mSensorObserver.dump(pw);
}
private void updateVoteLocked(int priority, Vote vote) {
@@ -2241,7 +2241,7 @@
}
}
- void dumpLocked(PrintWriter pw) {
+ void dump(PrintWriter pw) {
pw.println(" SensorObserver");
synchronized (mSensorObserverLock) {
pw.println(" mIsProxActive=" + mIsProxActive);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 86c9ca9..5186744 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -233,8 +233,6 @@
info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
- info.shouldConstrainMetricsForLauncher =
- mOverrideDisplayInfo.shouldConstrainMetricsForLauncher;
}
mInfo.set(info);
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 0fbc3e8..f0093bd 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -26,7 +26,6 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
@@ -41,7 +40,6 @@
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.Set;
import java.util.function.Consumer;
/**
@@ -267,61 +265,6 @@
return mDisplayGroups.get(groupId);
}
- /**
- * Returns the set of {@link DisplayInfo} for this device state, only fetching the info that is
- * part of the same display group as the provided display id. The DisplayInfo represent the
- * logical display layouts possible for the given device state.
- *
- * @param deviceState the state to query possible layouts for
- * @param displayId the display id to apply to all displays within the group
- * @param groupId the display group to filter display info for. Must be the same group as
- * the display with the provided display id.
- */
- public Set<DisplayInfo> getDisplayInfoForStateLocked(int deviceState, int displayId,
- int groupId) {
- Set<DisplayInfo> displayInfos = new ArraySet<>();
- final Layout layout = mDeviceStateToLayoutMap.get(deviceState);
- final int layoutSize = layout.size();
- for (int i = 0; i < layoutSize; i++) {
- Layout.Display displayLayout = layout.getAt(i);
- if (displayLayout == null) {
- continue;
- }
-
- // If the underlying display-device we want to use for this display
- // doesn't exist, then skip it. This can happen at startup as display-devices
- // trickle in one at a time. When the new display finally shows up, the layout is
- // recalculated so that the display is properly added to the current layout.
- final DisplayAddress address = displayLayout.getAddress();
- final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
- if (device == null) {
- Slog.w(TAG, "The display device (" + address + "), is not available"
- + " for the display state " + deviceState);
- continue;
- }
-
- // Find or create the LogicalDisplay to map the DisplayDevice to.
- final int logicalDisplayId = displayLayout.getLogicalDisplayId();
- final LogicalDisplay logicalDisplay = getDisplayLocked(logicalDisplayId);
- if (logicalDisplay == null) {
- Slog.w(TAG, "The logical display (" + address + "), is not available"
- + " for the display state " + deviceState);
- continue;
- }
- final DisplayInfo temp = logicalDisplay.getDisplayInfoLocked();
- DisplayInfo displayInfo = new DisplayInfo(temp);
- if (displayInfo.displayGroupId != groupId) {
- // Ignore any displays not in the provided group.
- continue;
- }
- // A display in the same group can be swapped out at any point, so set the display id
- // for all results to the provided display id.
- displayInfo.displayId = displayId;
- displayInfos.add(displayInfo);
- }
- return displayInfos;
- }
-
public void dumpLocked(PrintWriter pw) {
pw.println("LogicalDisplayMapper:");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9d6678053..1516739 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -542,9 +542,6 @@
*/
private InputMethodSubtype mCurrentSubtype;
- // Was the keyguard locked when this client became current?
- private boolean mCurClientInKeyguard;
-
/**
* {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
*/
@@ -2363,14 +2360,9 @@
mImeHiddenByDisplayPolicy = false;
if (mCurClient != cs) {
- // Was the keyguard locked when switching over to the new client?
- mCurClientInKeyguard = isKeyguardLocked();
// If the client is changing, we need to switch over to the new
// one.
unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT);
- if (DEBUG) Slog.v(TAG, "switching to client: client="
- + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
-
// If the screen is on, inform the new client it is active
if (mIsInteractive) {
scheduleSetActiveToClient(cs, true /* active */, false /* fullscreen */,
@@ -2838,7 +2830,7 @@
return;
}
final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
- if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
+ if (targetWindow != null) {
mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
}
mLastImeTargetWindow = targetWindow;
@@ -2875,12 +2867,13 @@
// all updateSystemUi happens on system previlege.
final long ident = Binder.clearCallingIdentity();
try {
- // apply policy for binder calls
- if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) {
- vis = 0;
- }
if (!mCurPerceptible) {
- vis &= ~InputMethodService.IME_VISIBLE;
+ if ((vis & InputMethodService.IME_VISIBLE) != 0) {
+ vis &= ~InputMethodService.IME_VISIBLE;
+ vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
+ }
+ } else {
+ vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
}
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 73baf79..82b34c3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -223,7 +223,7 @@
public Context getSettingsContext(int displayId) {
if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
final Context systemUiContext = ActivityThread.currentActivityThread()
- .createSystemUiContext(displayId);
+ .getSystemUiContext(displayId);
final Context windowContext = systemUiContext.createWindowContext(
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
mSettingsContext = new ContextThemeWrapper(
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 8460d67..1781588 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -52,8 +52,6 @@
private class GnssMeasurementListenerRegistration extends GnssListenerRegistration {
- private static final String GNSS_MEASUREMENTS_BUCKET = "gnss_measurement";
-
protected GnssMeasurementListenerRegistration(
@Nullable GnssMeasurementRequest request,
CallerIdentity callerIdentity,
@@ -70,15 +68,13 @@
@Nullable
@Override
protected void onActive() {
- mLocationAttributionHelper.reportHighPowerLocationStart(
- getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+ mLocationAttributionHelper.reportHighPowerLocationStart(getIdentity());
}
@Nullable
@Override
protected void onInactive() {
- mLocationAttributionHelper.reportHighPowerLocationStop(
- getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+ mLocationAttributionHelper.reportHighPowerLocationStop(getIdentity());
}
}
diff --git a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
index 5cb360b..4838752 100644
--- a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
@@ -24,55 +24,23 @@
import android.location.util.identity.CallerIdentity;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
/**
* Helps manage appop monitoring for multiple location clients.
*/
public class LocationAttributionHelper {
- private static class BucketKey {
- private final String mBucket;
- private final Object mKey;
-
- private BucketKey(String bucket, Object key) {
- mBucket = Objects.requireNonNull(bucket);
- mKey = Objects.requireNonNull(key);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- BucketKey that = (BucketKey) o;
- return mBucket.equals(that.mBucket)
- && mKey.equals(that.mKey);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mBucket, mKey);
- }
- }
-
private final AppOpsHelper mAppOpsHelper;
@GuardedBy("this")
- private final Map<CallerIdentity, Set<BucketKey>> mAttributions;
+ private final Map<CallerIdentity, Integer> mAttributions;
@GuardedBy("this")
- private final Map<CallerIdentity, Set<BucketKey>> mHighPowerAttributions;
+ private final Map<CallerIdentity, Integer> mHighPowerAttributions;
public LocationAttributionHelper(AppOpsHelper appOpsHelper) {
mAppOpsHelper = appOpsHelper;
@@ -84,15 +52,16 @@
/**
* Report normal location usage for the given caller in the given bucket, with a unique key.
*/
- public synchronized void reportLocationStart(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mAttributions.computeIfAbsent(identity,
- i -> new ArraySet<>());
- boolean empty = keySet.isEmpty();
- if (keySet.add(new BucketKey(bucket, key)) && empty) {
- if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
- mAttributions.remove(identity);
+ public synchronized void reportLocationStart(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mAttributions.getOrDefault(identity, 0);
+ if (count == 0) {
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
+ mAttributions.put(identity, 1);
}
+ } else {
+ mAttributions.put(identity, count + 1);
}
}
@@ -100,13 +69,15 @@
* Report normal location usage has stopped for the given caller in the given bucket, with a
* unique key.
*/
- public synchronized void reportLocationStop(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mAttributions.get(identity);
- if (keySet != null && keySet.remove(new BucketKey(bucket, key))
- && keySet.isEmpty()) {
+ public synchronized void reportLocationStop(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mAttributions.getOrDefault(identity, 0);
+ if (count == 1) {
mAttributions.remove(identity);
mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
+ } else if (count > 1) {
+ mAttributions.put(identity, count - 1);
}
}
@@ -114,19 +85,19 @@
* Report high power location usage for the given caller in the given bucket, with a unique
* key.
*/
- public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mHighPowerAttributions.computeIfAbsent(identity,
- i -> new ArraySet<>());
- boolean empty = keySet.isEmpty();
- if (keySet.add(new BucketKey(bucket, key)) && empty) {
- if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
- if (D) {
- Log.v(TAG, "starting high power location attribution for " + identity);
- }
- } else {
- mHighPowerAttributions.remove(identity);
+ public synchronized void reportHighPowerLocationStart(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mHighPowerAttributions.getOrDefault(identity, 0);
+ if (count == 0) {
+ if (D) {
+ Log.v(TAG, "starting high power location attribution for " + identity);
}
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+ mHighPowerAttributions.put(identity, 1);
+ }
+ } else {
+ mHighPowerAttributions.put(identity, count + 1);
}
}
@@ -134,16 +105,18 @@
* Report high power location usage has stopped for the given caller in the given bucket,
* with a unique key.
*/
- public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mHighPowerAttributions.get(identity);
- if (keySet != null && keySet.remove(new BucketKey(bucket, key))
- && keySet.isEmpty()) {
+ public synchronized void reportHighPowerLocationStop(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mHighPowerAttributions.getOrDefault(identity, 0);
+ if (count == 1) {
if (D) {
Log.v(TAG, "stopping high power location attribution for " + identity);
}
mHighPowerAttributions.remove(identity);
mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
+ } else if (count > 1) {
+ mHighPowerAttributions.put(identity, count - 1);
}
}
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index f7c4d03..3c5c5dd 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -31,6 +31,7 @@
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static android.os.UserHandle.USER_CURRENT;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
@@ -177,7 +178,7 @@
protected interface LocationTransport {
void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback) throws Exception;
+ @Nullable IRemoteCallback onCompleteCallback) throws Exception;
void deliverOnFlushComplete(int requestCode) throws Exception;
}
@@ -197,9 +198,8 @@
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback) throws RemoteException {
- mListener.onLocationChanged(locationResult.asList(),
- SingleUseCallback.wrap(onCompleteCallback));
+ @Nullable IRemoteCallback onCompleteCallback) throws RemoteException {
+ mListener.onLocationChanged(locationResult.asList(), onCompleteCallback);
}
@Override
@@ -227,7 +227,7 @@
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback)
+ @Nullable IRemoteCallback onCompleteCallback)
throws PendingIntent.CanceledException {
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setDontSendToRestrictedApps(true);
@@ -243,20 +243,34 @@
intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
}
+ PendingIntent.OnFinished onFinished = null;
+
// send() SHOULD only run the completion callback if it completes successfully. however,
- // b/199464864 (which could not be fixed in the S timeframe) means that it's possible
+ // b/201299281 (which could not be fixed in the S timeframe) means that it's possible
// for send() to throw an exception AND run the completion callback. if this happens, we
// would over-release the wakelock... we take matters into our own hands to ensure that
// the completion callback can only be run if send() completes successfully. this means
// the completion callback may be run inline - but as we've never specified what thread
// the callback is run on, this is fine.
- GatedCallback gatedCallback = new GatedCallback(onCompleteCallback);
+ GatedCallback gatedCallback;
+ if (onCompleteCallback != null) {
+ gatedCallback = new GatedCallback(() -> {
+ try {
+ onCompleteCallback.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ });
+ onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run();
+ } else {
+ gatedCallback = new GatedCallback(null);
+ }
mPendingIntent.send(
mContext,
0,
intent,
- (pI, i, rC, rD, rE) -> gatedCallback.run(),
+ onFinished,
null,
null,
options.toBundle());
@@ -293,7 +307,7 @@
@Override
public void deliverOnLocationChanged(@Nullable LocationResult locationResult,
- @Nullable Runnable onCompleteCallback)
+ @Nullable IRemoteCallback onCompleteCallback)
throws RemoteException {
// ILocationCallback doesn't currently support completion callbacks
Preconditions.checkState(onCompleteCallback == null);
@@ -398,7 +412,7 @@
EVENT_LOG.logProviderClientActive(mName, getIdentity());
if (!getRequest().isHiddenFromAppOps()) {
- mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
+ mLocationAttributionHelper.reportLocationStart(getIdentity());
}
onHighPowerUsageChanged();
@@ -413,7 +427,7 @@
onHighPowerUsageChanged();
if (!getRequest().isHiddenFromAppOps()) {
- mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
+ mLocationAttributionHelper.reportLocationStop(getIdentity());
}
onProviderListenerInactive();
@@ -488,10 +502,10 @@
if (!getRequest().isHiddenFromAppOps()) {
if (mIsUsingHighPower) {
mLocationAttributionHelper.reportHighPowerLocationStart(
- getIdentity(), getName(), getKey());
+ getIdentity());
} else {
mLocationAttributionHelper.reportHighPowerLocationStop(
- getIdentity(), getName(), getKey());
+ getIdentity());
}
}
}
@@ -714,6 +728,13 @@
final PowerManager.WakeLock mWakeLock;
+ // b/206340085 - if we allocate a new wakelock releaser object for every delivery we
+ // increase the risk of resource starvation. if a client stops processing deliveries the
+ // system server binder allocation pool will be starved as we continue to queue up
+ // deliveries, each with a new allocation. in order to mitigate this, we use a single
+ // releaser object per registration rather than per delivery.
+ final ExternalWakeLockReleaser mWakeLockReleaser;
+
private volatile ProviderTransport mProviderTransport;
private int mNumLocationsDelivered = 0;
private long mExpirationRealtimeMs = Long.MAX_VALUE;
@@ -727,6 +748,7 @@
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
mWakeLock.setReferenceCounted(true);
mWakeLock.setWorkSource(request.getWorkSource());
+ mWakeLockReleaser = new ExternalWakeLockReleaser(identity, mWakeLock);
}
@Override
@@ -872,6 +894,10 @@
MAX_FASTEST_INTERVAL_JITTER_MS);
if (deltaMs
< getRequest().getMinUpdateIntervalMillis() - maxJitterMs) {
+ if (D) {
+ Log.v(TAG, mName + " provider registration " + getIdentity()
+ + " dropped delivery - too fast");
+ }
return false;
}
@@ -881,6 +907,10 @@
if (smallestDisplacementM > 0.0 && location.distanceTo(
mPreviousLocation)
<= smallestDisplacementM) {
+ if (D) {
+ Log.v(TAG, mName + " provider registration " + getIdentity()
+ + " dropped delivery - too close");
+ }
return false;
}
}
@@ -898,7 +928,8 @@
if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()),
getIdentity())) {
if (D) {
- Log.w(TAG, "noteOp denied for " + getIdentity());
+ Log.w(TAG,
+ mName + " provider registration " + getIdentity() + " noteOp denied");
}
return null;
}
@@ -943,7 +974,7 @@
}
listener.deliverOnLocationChanged(deliverLocationResult,
- mUseWakeLock ? mWakeLock::release : null);
+ mUseWakeLock ? mWakeLockReleaser : null);
EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@@ -1482,7 +1513,7 @@
public boolean isEnabled(int userId) {
if (userId == UserHandle.USER_NULL) {
return false;
- } else if (userId == UserHandle.USER_CURRENT) {
+ } else if (userId == USER_CURRENT) {
return isEnabled(mUserHelper.getCurrentUserId());
}
@@ -1655,7 +1686,7 @@
}
}
return lastLocation;
- } else if (userId == UserHandle.USER_CURRENT) {
+ } else if (userId == USER_CURRENT) {
return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel,
isBypass, maximumAgeMs);
}
@@ -1700,7 +1731,7 @@
setLastLocation(location, runningUserIds[i]);
}
return;
- } else if (userId == UserHandle.USER_CURRENT) {
+ } else if (userId == USER_CURRENT) {
setLastLocation(location, mUserHelper.getCurrentUserId());
return;
}
@@ -2383,13 +2414,13 @@
filtered = locationResult.filter(location -> {
if (!location.isMock()) {
if (location.getLatitude() == 0 && location.getLongitude() == 0) {
- Log.w(TAG, "blocking 0,0 location from " + mName + " provider");
+ Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
return false;
}
}
if (!location.isComplete()) {
- Log.w(TAG, "blocking incomplete location from " + mName + " provider");
+ Log.e(TAG, "blocking incomplete location from " + mName + " provider");
return false;
}
@@ -2407,6 +2438,12 @@
filtered = locationResult;
}
+ Location last = getLastLocationUnsafe(USER_CURRENT, PERMISSION_FINE, true, Long.MAX_VALUE);
+ if (last != null && locationResult.get(0).getElapsedRealtimeNanos()
+ < last.getElapsedRealtimeNanos()) {
+ Log.e(TAG, "non-monotonic location received from " + mName + " provider");
+ }
+
// update last location
setLastLocation(filtered.getLastLocation(), UserHandle.USER_ALL);
@@ -2761,7 +2798,7 @@
@GuardedBy("this")
private boolean mRun;
- GatedCallback(Runnable callback) {
+ GatedCallback(@Nullable Runnable callback) {
mCallback = callback;
}
@@ -2796,4 +2833,27 @@
}
}
}
+
+ private static class ExternalWakeLockReleaser extends IRemoteCallback.Stub {
+
+ private final CallerIdentity mIdentity;
+ private final PowerManager.WakeLock mWakeLock;
+
+ ExternalWakeLockReleaser(CallerIdentity identity, PowerManager.WakeLock wakeLock) {
+ mIdentity = identity;
+ mWakeLock = Objects.requireNonNull(wakeLock);
+ }
+
+ @Override
+ public void sendResult(Bundle data) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mWakeLock.release();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "wakelock over-released by " + mIdentity, e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 22a675a..5e38bca 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -23,6 +23,8 @@
import static com.android.server.location.LocationManagerService.TAG;
import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
+import static java.lang.Math.max;
+
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
@@ -53,6 +55,7 @@
implements DeviceIdleHelper.DeviceIdleListener, DeviceIdleInternal.StationaryListener {
private static final long MAX_STATIONARY_LOCATION_AGE_MS = 30000;
+ private static final long MIN_INTERVAL_MS = 1000;
final Object mLock = new Object();
@@ -179,7 +182,7 @@
&& mLastLocation != null
&& mLastLocation.getElapsedRealtimeAgeMillis(mDeviceStationaryRealtimeMs)
<= MAX_STATIONARY_LOCATION_AGE_MS) {
- throttlingIntervalMs = mIncomingRequest.getIntervalMillis();
+ throttlingIntervalMs = max(mIncomingRequest.getIntervalMillis(), MIN_INTERVAL_MS);
}
ProviderRequest newRequest;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 20687c6..cfefffc 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4899,6 +4899,8 @@
? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
+ newAllowedReasons |= (uidBlockedState.allowedReasons
+ & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
if (LOGV) {
Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7ed897d..7455cec 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -266,7 +266,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
@@ -615,12 +614,6 @@
private NotificationRecordLogger mNotificationRecordLogger;
private InstanceIdSequence mNotificationInstanceIdSequence;
private Set<String> mMsgPkgsAllowedAsConvos = new HashSet();
- protected static final String ACTION_ENABLE_NAS =
- "android.server.notification.action.ENABLE_NAS";
- protected static final String ACTION_DISABLE_NAS =
- "android.server.notification.action.DISABLE_NAS";
- protected static final String ACTION_LEARNMORE_NAS =
- "android.server.notification.action.LEARNMORE_NAS";
static class Archive {
final SparseArray<Boolean> mEnabled;
@@ -755,97 +748,27 @@
setDefaultAssistantForUser(userId);
}
- protected void migrateDefaultNASShowNotificationIfNecessary() {
+ protected void migrateDefaultNAS() {
final List<UserInfo> activeUsers = mUm.getUsers();
for (UserInfo userInfo : activeUsers) {
int userId = userInfo.getUserHandle().getIdentifier();
if (isNASMigrationDone(userId) || mUm.isManagedProfile(userId)) {
continue;
}
- if (mAssistants.hasUserSet(userId)) {
- ComponentName defaultFromConfig = mAssistants.getDefaultFromConfig();
- List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
- if (allowedComponents.size() == 0) {
- setNASMigrationDone(userId);
- mAssistants.clearDefaults();
- continue;
- } else if (allowedComponents.contains(defaultFromConfig)) {
- setNASMigrationDone(userId);
- mAssistants.resetDefaultFromConfig();
- continue;
- }
- // TODO(b/192450820): re-enable when "user set" isn't over triggering
- //User selected different NAS, need onboarding
- /*enqueueNotificationInternal(getContext().getPackageName(),
- getContext().getOpPackageName(), Binder.getCallingUid(),
- Binder.getCallingPid(), TAG,
- SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE,
- createNASUpgradeNotification(userId), userId);*/
+ List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
+ if (allowedComponents.size() == 0) { // user set to none
+ Slog.d(TAG, "NAS Migration: user set to none, disable new NAS setting");
+ setNASMigrationDone(userId);
+ mAssistants.clearDefaults();
+ } else {
+ Slog.d(TAG, "Reset NAS setting and migrate to new default");
+ resetAssistantUserSet(userId);
+ // migrate to new default and set migration done
+ mAssistants.resetDefaultAssistantsIfNecessary();
}
}
}
- protected Notification createNASUpgradeNotification(int userId) {
- final Bundle extras = new Bundle();
- extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
- getContext().getResources().getString(R.string.global_action_settings));
- int title = R.string.nas_upgrade_notification_title;
- int content = R.string.nas_upgrade_notification_content;
-
- Intent onboardingIntent = new Intent(Settings.ACTION_NOTIFICATION_ASSISTANT_SETTINGS);
- onboardingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-
- Intent enableIntent = new Intent(ACTION_ENABLE_NAS);
- enableIntent.putExtra(Intent.EXTRA_USER_ID, userId);
- PendingIntent enableNASPendingIntent = PendingIntent.getBroadcast(getContext(),
- 0, enableIntent, PendingIntent.FLAG_IMMUTABLE);
-
- Intent disableIntent = new Intent(ACTION_DISABLE_NAS);
- disableIntent.putExtra(Intent.EXTRA_USER_ID, userId);
- PendingIntent disableNASPendingIntent = PendingIntent.getBroadcast(getContext(),
- 0, disableIntent, PendingIntent.FLAG_IMMUTABLE);
-
- Intent learnMoreIntent = new Intent(ACTION_LEARNMORE_NAS);
- learnMoreIntent.putExtra(Intent.EXTRA_USER_ID, userId);
- PendingIntent learnNASPendingIntent = PendingIntent.getBroadcast(getContext(),
- 0, learnMoreIntent, PendingIntent.FLAG_IMMUTABLE);
-
- Notification.Action enableNASAction = new Notification.Action.Builder(
- 0,
- getContext().getResources().getString(
- R.string.nas_upgrade_notification_enable_action),
- enableNASPendingIntent).build();
-
- Notification.Action disableNASAction = new Notification.Action.Builder(
- 0,
- getContext().getResources().getString(
- R.string.nas_upgrade_notification_disable_action),
- disableNASPendingIntent).build();
-
- Notification.Action learnMoreNASAction = new Notification.Action.Builder(
- 0,
- getContext().getResources().getString(
- R.string.nas_upgrade_notification_learn_more_action),
- learnNASPendingIntent).build();
-
-
- return new Notification.Builder(getContext(), SystemNotificationChannels.SYSTEM_CHANGES)
- .setAutoCancel(false)
- .setOngoing(true)
- .setTicker(getContext().getResources().getString(title))
- .setSmallIcon(R.drawable.ic_settings_24dp)
- .setContentTitle(getContext().getResources().getString(title))
- .setContentText(getContext().getResources().getString(content))
- .setContentIntent(PendingIntent.getActivity(getContext(), 0, onboardingIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
- .setLocalOnly(true)
- .setStyle(new Notification.BigTextStyle())
- .addAction(enableNASAction)
- .addAction(disableNASAction)
- .addAction(learnMoreNASAction)
- .build();
- }
-
@VisibleForTesting
void setNASMigrationDone(int baseUserId) {
for (int profileId : mUm.getProfileIds(baseUserId, false)) {
@@ -1861,41 +1784,6 @@
}
};
- private final BroadcastReceiver mNASIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
- if (ACTION_ENABLE_NAS.equals(action)) {
- mAssistants.resetDefaultFromConfig();
- setNotificationAssistantAccessGrantedForUserInternal(
- CollectionUtils.firstOrNull(mAssistants.getDefaultComponents()),
- userId, true, true);
- setNASMigrationDone(userId);
- cancelNotificationInternal(getContext().getPackageName(),
- getContext().getOpPackageName(), Binder.getCallingUid(),
- Binder.getCallingPid(), TAG,
- SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE, userId);
- } else if (ACTION_DISABLE_NAS.equals(action)) {
- //Set default NAS to be null if user selected none during migration
- mAssistants.clearDefaults();
- setNotificationAssistantAccessGrantedForUserInternal(
- null, userId, true, true);
- setNASMigrationDone(userId);
- cancelNotificationInternal(getContext().getPackageName(),
- getContext().getOpPackageName(), Binder.getCallingUid(),
- Binder.getCallingPid(), TAG,
- SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE, userId);
- } else if (ACTION_LEARNMORE_NAS.equals(action)) {
- Intent i = new Intent(getContext(), NASLearnMoreActivity.class);
- i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().sendBroadcastAsUser(
- new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.of(userId));
- getContext().startActivity(i);
- }
- }
- };
-
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_BADGING_URI
= Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
@@ -2407,12 +2295,6 @@
IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
-
- IntentFilter nasFilter = new IntentFilter();
- nasFilter.addAction(ACTION_ENABLE_NAS);
- nasFilter.addAction(ACTION_DISABLE_NAS);
- nasFilter.addAction(ACTION_LEARNMORE_NAS);
- getContext().registerReceiver(mNASIntentReceiver, nasFilter);
}
/**
@@ -2424,7 +2306,6 @@
getContext().unregisterReceiver(mNotificationTimeoutReceiver);
getContext().unregisterReceiver(mRestoreReceiver);
getContext().unregisterReceiver(mLocaleChangeReceiver);
- getContext().unregisterReceiver(mNASIntentReceiver);
if (mDeviceConfigChangedListener != null) {
DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
@@ -2691,7 +2572,7 @@
mConditionProviders.onBootPhaseAppsCanStart();
mHistoryManager.onBootPhaseAppsCanStart();
registerDeviceConfigChange();
- migrateDefaultNASShowNotificationIfNecessary();
+ migrateDefaultNAS();
} else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis());
}
@@ -5229,10 +5110,6 @@
public void setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig) {
checkCallerIsSystem();
setNASMigrationDone(userId);
- cancelNotificationInternal(getContext().getPackageName(),
- getContext().getOpPackageName(), Binder.getCallingUid(),
- Binder.getCallingPid(), TAG,
- SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE, userId);
if (loadFromConfig) {
mAssistants.resetDefaultFromConfig();
} else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 979b77f..7a70bfe 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15355,7 +15355,8 @@
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
- mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+ mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
+ | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
@@ -24298,24 +24299,24 @@
}
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
true /* checkShell */, "stop package");
- boolean shouldUnhibernate = false;
// writer
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.getStopped(userId) && !stopped) {
- shouldUnhibernate = true;
- }
if (!shouldFilterApplicationLocked(ps, callingUid, userId)
&& mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
- if (shouldUnhibernate) {
+ // If this would cause the app to leave force-stop, then also make sure to unhibernate the
+ // app if needed.
+ if (!stopped) {
mHandler.post(() -> {
AppHibernationManagerInternal ah =
mInjector.getLocalService(AppHibernationManagerInternal.class);
- ah.setHibernatingForUser(packageName, userId, false);
- ah.setHibernatingGlobally(packageName, false);
+ if (ah != null && ah.isHibernatingForUser(packageName, userId)) {
+ ah.setHibernatingForUser(packageName, userId, false);
+ ah.setHibernatingGlobally(packageName, false);
+ }
});
}
}
@@ -28824,8 +28825,12 @@
@Override
public void setSplashScreenTheme(@NonNull String packageName, @Nullable String themeId,
int userId) {
- int callingUid = Binder.getCallingUid();
- PackageSetting packageSetting = getPackageSettingForUser(packageName, callingUid, userId);
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "setSplashScreenTheme");
+ enforceOwnerRights(packageName, callingUid);
+ final PackageSetting packageSetting = getPackageSettingForUser(packageName, callingUid,
+ userId);
if (packageSetting != null) {
packageSetting.setSplashScreenTheme(userId, themeId);
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 7096f6f..cb28637 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -434,7 +434,8 @@
|| !pm.isGranted(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
pkg, UserHandle.of(userId))
|| !pm.isGranted(Manifest.permission.READ_PHONE_STATE, pkg,
- UserHandle.of(userId))) {
+ UserHandle.of(userId))
+ || pm.isSysComponentOrPersistentPlatformSignedPrivApp(pkg)) {
continue;
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 18c45e4..8b46906 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -196,9 +196,8 @@
}
private static boolean isHotwordDetectionServiceRequired(PackageManager pm) {
- // The HotwordDetectionService APIs aren't ready yet for Auto or TV.
- return !(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
- || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+ // Usage of the HotwordDetectionService won't be enforced until a later release.
+ return false;
}
@Override
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 268de3e..68e078c 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -17,11 +17,13 @@
import static android.view.KeyEvent.KEYCODE_POWER;
+import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseLongArray;
import android.view.KeyEvent;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ToBooleanFunction;
import java.io.PrintWriter;
@@ -35,13 +37,18 @@
private static final String TAG = "KeyCombinationManager";
// Store the received down time of keycode.
+ @GuardedBy("mLock")
private final SparseLongArray mDownTimes = new SparseLongArray(2);
private final ArrayList<TwoKeysCombinationRule> mRules = new ArrayList();
// Selected rules according to current key down.
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private final ArrayList<TwoKeysCombinationRule> mActiveRules = new ArrayList();
// The rule has been triggered by current keys.
+ @GuardedBy("mLock")
private TwoKeysCombinationRule mTriggeredRule;
+ private final Handler mHandler = new Handler();
// Keys in a key combination must be pressed within this interval of each other.
private static final long COMBINE_KEY_DELAY_MILLIS = 150;
@@ -109,6 +116,12 @@
* Return true if any active rule could be triggered by the key event, otherwise false.
*/
boolean interceptKey(KeyEvent event, boolean interactive) {
+ synchronized (mLock) {
+ return interceptKeyLocked(event, interactive);
+ }
+ }
+
+ private boolean interceptKeyLocked(KeyEvent event, boolean interactive) {
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final int keyCode = event.getKeyCode();
final int count = mActiveRules.size();
@@ -154,7 +167,7 @@
return false;
}
Log.v(TAG, "Performing combination rule : " + rule);
- rule.execute();
+ mHandler.post(rule::execute);
mTriggeredRule = rule;
return true;
});
@@ -169,7 +182,7 @@
for (int index = count - 1; index >= 0; index--) {
final TwoKeysCombinationRule rule = mActiveRules.get(index);
if (rule.shouldInterceptKey(keyCode)) {
- rule.cancel();
+ mHandler.post(rule::cancel);
mActiveRules.remove(index);
}
}
@@ -181,31 +194,37 @@
* Return the interceptTimeout to tell InputDispatcher when is ready to deliver to window.
*/
long getKeyInterceptTimeout(int keyCode) {
- if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
- return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+ synchronized (mLock) {
+ if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
+ return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+ }
+ return 0;
}
- return 0;
}
/**
* True if the key event had been handled.
*/
boolean isKeyConsumed(KeyEvent event) {
- if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
- return false;
+ synchronized (mLock) {
+ if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
+ return false;
+ }
+ return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
}
- return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
}
/**
* True if power key is the candidate.
*/
boolean isPowerKeyIntercepted() {
- if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
- // return false if only if power key pressed.
- return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+ synchronized (mLock) {
+ if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
+ // return false if only if power key pressed.
+ return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+ }
+ return false;
}
- return false;
}
/**
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
index 212f81f..086ebc9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
@@ -27,8 +27,6 @@
import android.util.Log;
import java.util.Objects;
-import java.util.Timer;
-import java.util.TimerTask;
/**
* An {@link ISoundTriggerHw2} decorator that would enforce deadlines on all calls and reboot the
@@ -38,14 +36,12 @@
private static final long TIMEOUT_MS = 3000;
private static final String TAG = "SoundTriggerHw2Watchdog";
- private final @NonNull
- ISoundTriggerHw2 mUnderlying;
- private final @NonNull
- Timer mTimer;
+ private final @NonNull ISoundTriggerHw2 mUnderlying;
+ private final @NonNull UptimeTimer mTimer;
public SoundTriggerHw2Watchdog(@NonNull ISoundTriggerHw2 underlying) {
mUnderlying = Objects.requireNonNull(underlying);
- mTimer = new Timer("SoundTriggerHw2Watchdog");
+ mTimer = new UptimeTimer("SoundTriggerHw2Watchdog");
}
@Override
@@ -149,21 +145,16 @@
}
private class Watchdog implements AutoCloseable {
- private final @NonNull
- TimerTask mTask;
+ private final @NonNull UptimeTimer.Task mTask;
// This exception is used merely for capturing a stack trace at the time of creation.
private final @NonNull
Exception mException = new Exception();
Watchdog() {
- mTask = new TimerTask() {
- @Override
- public void run() {
- Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
- rebootHal();
- }
- };
- mTimer.schedule(mTask, TIMEOUT_MS);
+ mTask = mTimer.createTask(() -> {
+ Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
+ rebootHal();
+ }, TIMEOUT_MS);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 9999aff..3fbcd93 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -125,16 +125,27 @@
* originator temporarily doesn't have the right permissions to use this service.
*/
private void enforcePermissionsForPreflight(@NonNull Identity identity) {
- enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO);
- enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD);
+ enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO,
+ /* allowSoftDenial= */ true);
+ enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD,
+ /* allowSoftDenial= */ true);
}
/**
* Throws a {@link SecurityException} iff the originator has permission to receive data.
*/
void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) {
- enforcePermissionForDataDelivery(mContext, identity, RECORD_AUDIO,
- reason);
+ // SoundTrigger data is treated the same as Hotword-source audio. This should incur the
+ // HOTWORD op instead of the RECORD_AUDIO op. The RECORD_AUDIO permission is still required,
+ // and since this is a data delivery check, soft denials aren't accepted.
+ // TODO(b/212458940): Find a better approach for checking the permission that doesn't
+ // require the client to know such details about the permissions logic.
+ enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO,
+ /* allowSoftDenial= */ false);
+ int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD);
+ mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp, identity.uid,
+ identity.packageName, identity.attributionTag, reason);
+
enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD,
reason);
}
@@ -163,20 +174,25 @@
/**
* Throws a {@link SecurityException} if originator permanently doesn't have the given
* permission.
- * Soft (temporary) denials are considered OK for preflight purposes.
*
- * @param context A {@link Context}, used for permission checks.
- * @param identity The identity to check.
- * @param permission The identifier of the permission we want to check.
+ * @param context A {@link Context}, used for permission checks.
+ * @param identity The identity to check.
+ * @param permission The identifier of the permission we want to check.
+ * @param allowSoftDenial If true, the operation succeeds even for soft (temporary) denials.
*/
+ // TODO: Consider splitting up this method instead of using `allowSoftDenial`, to make it
+ // clearer when soft denials are not allowed.
private static void enforcePermissionForPreflight(@NonNull Context context,
- @NonNull Identity identity, @NonNull String permission) {
+ @NonNull Identity identity, @NonNull String permission, boolean allowSoftDenial) {
final int status = PermissionUtil.checkPermissionForPreflight(context, identity,
permission);
switch (status) {
case PermissionChecker.PERMISSION_GRANTED:
- case PermissionChecker.PERMISSION_SOFT_DENIED:
return;
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
+ if (allowSoftDenial) {
+ return;
+ } // else fall through
case PermissionChecker.PERMISSION_HARD_DENIED:
throw new SecurityException(
String.format("Failed to obtain permission %s for identity %s", permission,
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java b/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
new file mode 100644
index 0000000..bfcc7d8
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A simple timer, similar to java.util.Timer, but using the "uptime clock".
+ *
+ * Example usage:
+ * UptimeTimer timer = new UptimeTimer("TimerThread");
+ * UptimeTimer.Task task = timer.createTask(() -> { ... }, 100);
+ * ...
+ * // optionally, some time later:
+ * task.cancel();
+ */
+class UptimeTimer {
+ private Handler mHandler = null;
+
+ interface Task {
+ void cancel();
+ }
+
+ UptimeTimer(String threadName) {
+ new Thread(this::threadFunc, threadName).start();
+ synchronized (this) {
+ while (mHandler == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ Task createTask(@NonNull Runnable runnable, long uptimeMs) {
+ TaskImpl task = new TaskImpl(runnable);
+ mHandler.postDelayed(task, uptimeMs);
+ return task;
+ }
+
+ private void threadFunc() {
+ Looper.prepare();
+ synchronized (this) {
+ mHandler = new Handler(Looper.myLooper());
+ notifyAll();
+ }
+ Looper.loop();
+ }
+
+ private static class TaskImpl implements Task, Runnable {
+ private AtomicReference<Runnable> mRunnable = new AtomicReference<>();
+
+ TaskImpl(@NonNull Runnable runnable) {
+ mRunnable.set(runnable);
+ }
+
+ @Override
+ public void cancel() {
+ mRunnable.set(null);
+ }
+
+ @Override
+ public void run() {
+ Runnable runnable = mRunnable.get();
+ if (runnable != null) {
+ runnable.run();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 2be29d4..dfff76d 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -50,6 +50,10 @@
import static android.util.MathUtils.constrain;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO;
@@ -4387,8 +4391,9 @@
final int userId = userInfo.getUserHandle().getIdentifier();
if (isAccessibilityShortcutUser(mContext, userId)) {
- final int software_shortcut_type = Settings.Secure.getIntForUser(resolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId);
+ final int software_shortcut_type = convertToAccessibilityShortcutType(
+ Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId));
final String software_shortcut_list = Settings.Secure.getStringForUser(resolver,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
final int software_shortcut_service_num = countAccessibilityServices(
@@ -4509,6 +4514,19 @@
&& !TextUtils.isEmpty(software_string);
}
+ private int convertToAccessibilityShortcutType(int shortcutType) {
+ switch (shortcutType) {
+ case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR:
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+ case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU:
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+ case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE:
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+ default:
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
+ }
+ }
+
// Thermal event received from vendor thermal management subsystem
private static final class ThermalEventListener extends IThermalEventListener.Stub {
@Override
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 7dec4e7..51c3b33 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -77,6 +77,7 @@
import android.os.PowerManager.WakeLock;
import android.os.Process;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.ArraySet;
import android.util.Slog;
@@ -782,8 +783,19 @@
// TODO(b/179091925): Move the delayed-message handling to BaseState
// If underlying is null, all underlying networks have been lost. Disconnect VCN after a
- // timeout.
+ // timeout (or immediately if in airplane mode, since the device user has indicated that
+ // the radios should all be turned off).
if (underlying == null) {
+ if (mDeps.isAirplaneModeOn(mVcnContext)) {
+ sendMessageAndAcquireWakeLock(
+ EVENT_UNDERLYING_NETWORK_CHANGED,
+ TOKEN_ALL,
+ new EventUnderlyingNetworkChangedInfo(null));
+ sendDisconnectRequestedAndAcquireWakelock(
+ DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */);
+ return;
+ }
+
setDisconnectRequestAlarm();
} else {
// Received a new Network so any previous alarm is irrelevant - cancel + clear it,
@@ -2414,6 +2426,12 @@
validationStatusCallback);
}
+ /** Checks if airplane mode is enabled. */
+ public boolean isAirplaneModeOn(@NonNull VcnContext vcnContext) {
+ return Settings.Global.getInt(vcnContext.getContext().getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+ }
+
/** Gets the elapsed real time since boot, in millis. */
public long getElapsedRealTime() {
return SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/wallpaper/LocalColorRepository.java b/services/core/java/com/android/server/wallpaper/LocalColorRepository.java
new file mode 100644
index 0000000..e6265f4
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/LocalColorRepository.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wallpaper;
+
+import android.app.ILocalWallpaperColorConsumer;
+import android.graphics.RectF;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Manages the lifecycle of local wallpaper color callbacks and their interested wallpaper regions.
+ */
+public class LocalColorRepository {
+ /**
+ * Maps local wallpaper color callbacks' binders to their interested wallpaper regions, which
+ * are stored in a map of display Ids to wallpaper regions.
+ * binder callback -> [display id: int] -> areas
+ */
+ ArrayMap<IBinder, SparseArray<ArraySet<RectF>>> mLocalColorAreas = new ArrayMap();
+ RemoteCallbackList<ILocalWallpaperColorConsumer> mCallbacks = new RemoteCallbackList();
+
+ /**
+ * Add areas to a consumer
+ * @param consumer
+ * @param areas
+ * @param displayId
+ */
+ public void addAreas(ILocalWallpaperColorConsumer consumer, List<RectF> areas, int displayId) {
+ IBinder binder = consumer.asBinder();
+ SparseArray<ArraySet<RectF>> displays = mLocalColorAreas.get(binder);
+ ArraySet<RectF> displayAreas = null;
+ if (displays == null) {
+ try {
+ consumer.asBinder().linkToDeath(() ->
+ mLocalColorAreas.remove(consumer.asBinder()), 0);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ displays = new SparseArray<>();
+ mLocalColorAreas.put(binder, displays);
+ } else {
+ displayAreas = displays.get(displayId);
+ }
+ if (displayAreas == null) {
+ displayAreas = new ArraySet(areas);
+ displays.put(displayId, displayAreas);
+ }
+
+ for (int i = 0; i < areas.size(); i++) {
+ displayAreas.add(areas.get(i));
+ }
+ mCallbacks.register(consumer);
+ }
+
+ /**
+ * remove an area for a consumer
+ * @param consumer
+ * @param areas
+ * @param displayId
+ * @return the areas that are removed from all callbacks
+ */
+ public List<RectF> removeAreas(ILocalWallpaperColorConsumer consumer, List<RectF> areas,
+ int displayId) {
+ IBinder binder = consumer.asBinder();
+ SparseArray<ArraySet<RectF>> displays = mLocalColorAreas.get(binder);
+ ArraySet<RectF> registeredAreas = null;
+ if (displays != null) {
+ registeredAreas = displays.get(displayId);
+ if (registeredAreas == null) {
+ mCallbacks.unregister(consumer);
+ } else {
+ for (int i = 0; i < areas.size(); i++) {
+ registeredAreas.remove(areas.get(i));
+ }
+ if (registeredAreas.size() == 0) {
+ displays.remove(displayId);
+ }
+ }
+ if (displays.size() == 0) {
+ mLocalColorAreas.remove(binder);
+ mCallbacks.unregister(consumer);
+ }
+ } else {
+ mCallbacks.unregister(consumer);
+ }
+ ArraySet<RectF> purged = new ArraySet<>(areas);
+ for (int i = 0; i < mLocalColorAreas.size(); i++) {
+ for (int j = 0; j < mLocalColorAreas.valueAt(i).size(); j++) {
+ for (int k = 0; k < mLocalColorAreas.valueAt(i).valueAt(j).size(); k++) {
+ purged.remove(mLocalColorAreas.valueAt(i).valueAt(j).valueAt(k));
+ }
+ }
+ }
+ return new ArrayList(purged);
+ }
+
+ /**
+ * Return the local areas by display id
+ * @param displayId
+ * @return
+ */
+ public List<RectF> getAreasByDisplayId(int displayId) {
+ ArrayList<RectF> areas = new ArrayList();
+ for (int i = 0; i < mLocalColorAreas.size(); i++) {
+ SparseArray<ArraySet<RectF>> displays = mLocalColorAreas.valueAt(i);
+ if (displays == null) continue;
+ ArraySet<RectF> displayAreas = displays.get(displayId);
+ if (displayAreas == null) continue;
+ for (int j = 0; j < displayAreas.size(); j++) {
+ areas.add(displayAreas.valueAt(j));
+ }
+ }
+ return areas;
+ }
+
+ /**
+ * invoke a callback for each area of interest
+ * @param callback
+ * @param area
+ * @param displayId
+ */
+ public void forEachCallback(Consumer<ILocalWallpaperColorConsumer> callback,
+ RectF area, int displayId) {
+ mCallbacks.broadcast(cb -> {
+ IBinder binder = cb.asBinder();
+ SparseArray<ArraySet<RectF>> displays = mLocalColorAreas.get(binder);
+ if (displays == null) return;
+ ArraySet<RectF> displayAreas = displays.get(displayId);
+ if (displayAreas != null && displayAreas.contains(area)) callback.accept(cb);
+ });
+ }
+
+ /**
+ * For testing
+ * @param callback
+ * @return if the callback is registered
+ */
+ @VisibleForTesting
+ protected boolean isCallbackAvailable(ILocalWallpaperColorConsumer callback) {
+ return mLocalColorAreas.get(callback.asBinder()) != null;
+ }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 2f353d1..6fda72e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -89,8 +89,6 @@
import android.service.wallpaper.WallpaperService;
import android.system.ErrnoException;
import android.system.Os;
-import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -881,12 +879,7 @@
private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
private int mCurrentUserId = UserHandle.USER_NULL;
private boolean mInAmbientMode;
- private ArrayMap<IBinder, ArraySet<RectF>> mLocalColorCallbackAreas =
- new ArrayMap<>();
- private ArrayMap<RectF, RemoteCallbackList<ILocalWallpaperColorConsumer>>
- mLocalColorAreaCallbacks = new ArrayMap<>();
- private ArrayMap<Integer, ArraySet<RectF>> mLocalColorDisplayIdAreas = new ArrayMap<>();
- private ArrayMap<IBinder, Integer> mLocalColorCallbackDisplayId = new ArrayMap<>();
+ private LocalColorRepository mLocalColorRepo = new LocalColorRepository();
static class WallpaperData {
@@ -1305,24 +1298,16 @@
public void onLocalWallpaperColorsChanged(RectF area, WallpaperColors colors,
int displayId) {
forEachDisplayConnector(displayConnector -> {
- if (displayConnector.mDisplayId == displayId) {
- RemoteCallbackList<ILocalWallpaperColorConsumer> callbacks;
- ArrayMap<IBinder, Integer> callbackDisplayIds;
- synchronized (mLock) {
- callbacks = mLocalColorAreaCallbacks.get(area);
- callbackDisplayIds = new ArrayMap<>(mLocalColorCallbackDisplayId);
+ Consumer<ILocalWallpaperColorConsumer> callback = cb -> {
+ try {
+ cb.onColorsChanged(area, colors);
+ } catch (RemoteException e) {
+ e.printStackTrace();
}
- if (callbacks == null) return;
- callbacks.broadcast(c -> {
- try {
- Integer targetDisplayId =
- callbackDisplayIds.get(c.asBinder());
- if (targetDisplayId == null) return;
- if (targetDisplayId == displayId) c.onColorsChanged(area, colors);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- });
+ };
+ synchronized (mLock) {
+ // it is safe to make an IPC call since it is one way (returns immediately)
+ mLocalColorRepo.forEachCallback(callback, area, displayId);
}
});
}
@@ -1491,10 +1476,10 @@
Slog.w(TAG, "Failed to request wallpaper colors", e);
}
- ArraySet<RectF> areas = mLocalColorDisplayIdAreas.get(displayId);
+ List<RectF> areas = mLocalColorRepo.getAreasByDisplayId(displayId);
if (areas != null && areas.size() != 0) {
try {
- connector.mEngine.addLocalColorsAreas(new ArrayList<>(areas));
+ connector.mEngine.addLocalColorsAreas(areas);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to register local colors areas", e);
}
@@ -2494,37 +2479,10 @@
}
IWallpaperEngine engine = getEngine(which, userId, displayId);
if (engine == null) return;
- ArrayList<RectF> validAreas = new ArrayList<>(regions.size());
synchronized (mLock) {
- ArraySet<RectF> areas = mLocalColorCallbackAreas.get(callback);
- if (areas == null) areas = new ArraySet<>(regions.size());
- areas.addAll(regions);
- mLocalColorCallbackAreas.put(callback.asBinder(), areas);
+ mLocalColorRepo.addAreas(callback, regions, displayId);
}
- for (int i = 0; i < regions.size(); i++) {
- if (!LOCAL_COLOR_BOUNDS.contains(regions.get(i))) {
- continue;
- }
- RemoteCallbackList callbacks;
- synchronized (mLock) {
- callbacks = mLocalColorAreaCallbacks.get(
- regions.get(i));
- if (callbacks == null) {
- callbacks = new RemoteCallbackList();
- mLocalColorAreaCallbacks.put(regions.get(i), callbacks);
- }
- mLocalColorCallbackDisplayId.put(callback.asBinder(), displayId);
- ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId);
- if (displayAreas == null) {
- displayAreas = new ArraySet<>(1);
- mLocalColorDisplayIdAreas.put(displayId, displayAreas);
- }
- displayAreas.add(regions.get(i));
- }
- validAreas.add(regions.get(i));
- callbacks.register(callback);
- }
- engine.addLocalColorsAreas(validAreas);
+ engine.addLocalColorsAreas(regions);
}
@Override
@@ -2539,45 +2497,19 @@
throw new SecurityException("calling user id does not match");
}
final long identity = Binder.clearCallingIdentity();
- ArrayList<RectF> purgeAreas = new ArrayList<>();
- IBinder binder = callback.asBinder();
+ List<RectF> purgeAreas = null;
try {
synchronized (mLock) {
- ArraySet<RectF> currentAreas = mLocalColorCallbackAreas.get(binder);
- if (currentAreas == null) return;
- currentAreas.removeAll(removeAreas);
- if (currentAreas.size() == 0) {
- mLocalColorCallbackDisplayId.remove(binder);
- for (RectF removeArea : removeAreas) {
- RemoteCallbackList<ILocalWallpaperColorConsumer> remotes =
- mLocalColorAreaCallbacks.get(removeArea);
- if (remotes == null) continue;
- remotes.unregister(callback);
- if (remotes.getRegisteredCallbackCount() == 0) {
- mLocalColorAreaCallbacks.remove(removeArea);
- purgeAreas.add(removeArea);
- ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId);
- if (displayAreas != null) {
- displayAreas.remove(removeArea);
- if (displayAreas.size() == 0) {
- mLocalColorDisplayIdAreas.remove(displayId);
- }
- }
- }
- }
- }
+ purgeAreas = mLocalColorRepo.removeAreas(callback, removeAreas, displayId);
}
-
} catch (Exception e) {
// ignore any exception
} finally {
Binder.restoreCallingIdentity(identity);
}
-
- if (purgeAreas.size() == 0) return;
IWallpaperEngine engine = getEngine(which, userId, displayId);
- if (engine == null) return;
- engine.removeLocalColorsAreas(purgeAreas);
+ if (engine == null || purgeAreas == null) return;
+ if (purgeAreas.size() > 0) engine.removeLocalColorsAreas(purgeAreas);
}
@Override
@@ -3075,12 +3007,35 @@
}
}
+ private boolean packageBelongsToUid(String packageName, int uid) {
+ int userId = UserHandle.getUserId(uid);
+ int packageUid;
+ try {
+ packageUid = mContext.getPackageManager().getPackageUidAsUser(
+ packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return packageUid == uid;
+ }
+
+ private void enforcePackageBelongsToUid(String packageName, int uid) {
+ if (!packageBelongsToUid(packageName, uid)) {
+ throw new IllegalArgumentException(
+ "Invalid package or package does not belong to uid:"
+ + uid);
+ }
+ }
+
/**
* Certain user types do not support wallpapers (e.g. managed profiles). The check is
* implemented through through the OP_WRITE_WALLPAPER AppOp.
*/
public boolean isWallpaperSupported(String callingPackage) {
- return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
+ final int callingUid = Binder.getCallingUid();
+ enforcePackageBelongsToUid(callingPackage, callingUid);
+
+ return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, callingUid,
callingPackage) == AppOpsManager.MODE_ALLOWED;
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ee72fc8..e9ffcc0 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -49,6 +49,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityClientController;
+import android.app.ICompatCameraControlCallback;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
@@ -766,6 +767,22 @@
Binder.restoreCallingIdentity(origId);
}
+ @Override
+ public void requestCompatCameraControl(IBinder token, boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+ if (r != null) {
+ r.updateCameraCompatState(showControl, transformationApplied, callback);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
/**
* Checks the state of the system and the activity associated with the given {@param token} to
* verify that picture-in-picture is supported for that activity.
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index f878562..614c532 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -61,6 +61,11 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_APPLY_TREATMENT;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_REVERT_TREATMENT;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_APPLY_TREATMENT;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_DISMISS;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_REVERT_TREATMENT;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
@@ -73,6 +78,8 @@
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.ActivityOptions.SourceInfo;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.app.WaitResult;
import android.app.WindowConfiguration.WindowingMode;
import android.content.ComponentName;
@@ -1377,6 +1384,71 @@
}
}
+ /**
+ * Logs the Camera Compat Control appeared event that corresponds to the given {@code state}
+ * with the given {@code packageUid}.
+ */
+ void logCameraCompatControlAppearedEventReported(@CameraCompatControlState int state,
+ int packageUid) {
+ switch (state) {
+ case TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED:
+ logCameraCompatControlEventReported(
+ CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_APPLY_TREATMENT,
+ packageUid);
+ break;
+ case TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED:
+ logCameraCompatControlEventReported(
+ CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_REVERT_TREATMENT,
+ packageUid);
+ break;
+ case TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN:
+ // Nothing to log.
+ break;
+ default:
+ Slog.w(TAG, "Unexpected state in logCameraCompatControlAppearedEventReported: "
+ + state);
+ break;
+ }
+ }
+
+ /**
+ * Logs the Camera Compat Control clicked event that corresponds to the given {@code state}
+ * with the given {@code packageUid}.
+ */
+ void logCameraCompatControlClickedEventReported(@CameraCompatControlState int state,
+ int packageUid) {
+ switch (state) {
+ case TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED:
+ logCameraCompatControlEventReported(
+ CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_APPLY_TREATMENT,
+ packageUid);
+ break;
+ case TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED:
+ logCameraCompatControlEventReported(
+ CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_REVERT_TREATMENT,
+ packageUid);
+ break;
+ case TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED:
+ logCameraCompatControlEventReported(
+ CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_DISMISS,
+ packageUid);
+ break;
+ default:
+ Slog.w(TAG, "Unexpected state in logCameraCompatControlAppearedEventReported: "
+ + state);
+ break;
+ }
+ }
+
+ private void logCameraCompatControlEventReported(int event, int packageUid) {
+ FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED, packageUid,
+ event);
+ if (DEBUG_METRICS) {
+ Slog.i(TAG, String.format("CAMERA_COMPAT_CONTROL_EVENT_REPORTED(%s, %s)", packageUid,
+ event));
+ }
+ }
+
private ArtManagerInternal getArtManagerInternal() {
if (mArtManagerInternal == null) {
// Note that this may be null.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 95a286c..48f81ff 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -160,6 +160,7 @@
import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS;
import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ENABLED;
import static com.android.server.wm.ActivityRecordProto.PROC_ID;
+import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS;
import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
@@ -234,9 +235,12 @@
import android.app.Activity;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
+import android.app.ICompatCameraControlCallback;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.app.WaitResult;
import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
@@ -261,6 +265,7 @@
import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ConstrainDisplayApisConfig;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -596,6 +601,8 @@
*/
private CompatDisplayInsets mCompatDisplayInsets;
+ private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
+
boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session
IVoiceInteractionSession voiceSession; // Voice interaction session for this activity
@@ -719,6 +726,20 @@
@Nullable
private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio;
+ // State of the Camera app compat control which is used to correct stretched viewfinder
+ // in apps that don't handle all possible configurations and changes between them correctly.
+ @CameraCompatControlState
+ private int mCameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+
+
+ // The callback that allows to ask the calling View to apply the treatment for stretched
+ // issues affecting camera viewfinders when the user clicks on the camera compat control.
+ @Nullable
+ private ICompatCameraControlCallback mCompatCameraControlCallback;
+
+ private final boolean mCameraCompatControlEnabled;
+ private boolean mCameraCompatControlClickedByUser;
+
// activity is not displayed?
// TODO: rename to mNoDisplay
@VisibleForTesting
@@ -1057,6 +1078,8 @@
pw.print(" forceNewConfig="); pw.println(forceNewConfig);
pw.print(prefix); pw.print("mActivityType=");
pw.println(activityTypeToString(getActivityType()));
+ pw.print(prefix); pw.print("mImeInsetsFrozenUntilStartInput=");
+ pw.println(mImeInsetsFrozenUntilStartInput);
if (requestedVrComponent != null) {
pw.print(prefix);
pw.print("requestedVrComponent=");
@@ -1160,14 +1183,20 @@
if (info.configChanges != 0) {
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
}
- pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis());
- pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis());
+ pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis(
+ sConstrainDisplayApisConfig));
+ pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis(
+ sConstrainDisplayApisConfig));
}
if (mLastParentBeforePip != null) {
pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
}
mLetterboxUiController.dump(pw, prefix);
+
+ pw.println(prefix + "mCameraCompatControlState="
+ + TaskInfo.cameraCompatControlStateToString(mCameraCompatControlState));
+ pw.println(prefix + "mCameraCompatControlEnabled=" + mCameraCompatControlEnabled);
}
static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
@@ -1387,9 +1416,7 @@
/** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */
private boolean shouldStartChangeTransition(
@Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) {
- if (mWmService.mDisableTransitionAnimation
- || mDisplayContent == null || newParent == null || oldParent == null
- || getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) {
+ if (newParent == null || oldParent == null || !canStartChangeTransition()) {
return false;
}
@@ -1569,6 +1596,95 @@
mLetterboxUiController.getLetterboxInnerBounds(outBounds);
}
+ void updateCameraCompatState(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return;
+ }
+ if (mCameraCompatControlClickedByUser && (showControl
+ || mCameraCompatControlState == TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED)) {
+ // The user already applied treatment on this activity or dismissed control.
+ // Respecting their choice.
+ return;
+ }
+ mCompatCameraControlCallback = callback;
+ int newCameraCompatControlState = !showControl
+ ? TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN
+ : transformationApplied
+ ? TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
+ : TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ boolean changed = setCameraCompatControlState(newCameraCompatControlState);
+ if (!changed) {
+ return;
+ }
+ mTaskSupervisor.getActivityMetricsLogger().logCameraCompatControlAppearedEventReported(
+ newCameraCompatControlState, info.applicationInfo.uid);
+ if (newCameraCompatControlState == TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN) {
+ mCameraCompatControlClickedByUser = false;
+ mCompatCameraControlCallback = null;
+ }
+ // Trigger TaskInfoChanged to update the camera compat UI.
+ getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+ }
+
+ void updateCameraCompatStateFromUser(@CameraCompatControlState int state) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return;
+ }
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN) {
+ Slog.w(TAG, "Unexpected hidden state in updateCameraCompatState");
+ return;
+ }
+ boolean changed = setCameraCompatControlState(state);
+ mCameraCompatControlClickedByUser = true;
+ if (!changed) {
+ return;
+ }
+ mTaskSupervisor.getActivityMetricsLogger().logCameraCompatControlClickedEventReported(
+ state, info.applicationInfo.uid);
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED) {
+ mCompatCameraControlCallback = null;
+ return;
+ }
+ if (mCompatCameraControlCallback == null) {
+ Slog.w(TAG, "Callback for a camera compat control is null");
+ return;
+ }
+ try {
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED) {
+ mCompatCameraControlCallback.applyCameraCompatTreatment();
+ } else {
+ mCompatCameraControlCallback.revertCameraCompatTreatment();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to apply or revert camera compat treatment", e);
+ }
+ }
+
+ private boolean setCameraCompatControlState(@CameraCompatControlState int state) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return false;
+ }
+ if (mCameraCompatControlState != state) {
+ mCameraCompatControlState = state;
+ return true;
+ }
+ return false;
+ }
+
+ @CameraCompatControlState
+ int getCameraCompatControlState() {
+ return mCameraCompatControlState;
+ }
+
+ @VisibleForTesting
+ boolean isCameraCompatControlEnabled() {
+ return mCameraCompatControlEnabled;
+ }
+
/**
* @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
* when the current activity is displayed.
@@ -1769,6 +1885,10 @@
info.windowLayout.windowLayoutAffinity =
uid + ":" + info.windowLayout.windowLayoutAffinity;
}
+ // Initialize once, when we know all system services are available.
+ if (sConstrainDisplayApisConfig == null) {
+ sConstrainDisplayApisConfig = new ConstrainDisplayApisConfig();
+ }
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;
@@ -1826,6 +1946,8 @@
taskDescription = _taskDescription;
mLetterboxUiController = new LetterboxUiController(mWmService, this);
+ mCameraCompatControlEnabled = mWmService.mContext.getResources()
+ .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
if (_createTime > 0) {
createTime = _createTime;
@@ -6565,6 +6687,13 @@
} else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON) {
return false;
}
+ // Choose the default behavior for Launcher and SystemUI when the SplashScreen style is
+ // not specified in the ActivityOptions.
+ if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME) {
+ return false;
+ } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+ return true;
+ }
}
if (sourceRecord == null) {
sourceRecord = searchCandidateLaunchingActivity();
@@ -6574,14 +6703,11 @@
return sourceRecord.mSplashScreenStyleEmpty;
}
- // If this activity was launched from a system surface, never use an empty splash screen
+ // If this activity was launched from Launcher or System for first start, never use an
+ // empty splash screen.
// Need to check sourceRecord before in case this activity is launched from service.
- if (launchedFromSystemSurface()) {
- return false;
- }
-
- // Otherwise use empty.
- return true;
+ return !startActivity || !(mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM
+ || mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME);
}
private int getSplashscreenTheme() {
@@ -7461,8 +7587,8 @@
+ "should create compatDisplayInsets = %s",
getUid(),
mTmpBounds,
- info.neverSandboxDisplayApis(),
- info.alwaysSandboxDisplayApis(),
+ info.neverSandboxDisplayApis(sConstrainDisplayApisConfig),
+ info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig),
!matchParentBounds(),
mCompatDisplayInsets != null,
shouldCreateCompatDisplayInsets());
@@ -7924,9 +8050,17 @@
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
final boolean fillContainer = resolvedBounds.equals(containingBounds);
final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
- final int screenPosY = mSizeCompatBounds == null
+ // If the activity is not in size compat mode, calculate vertical centering
+ // from the container and resolved bounds.
+ // If the activity is in size compat mode, calculate vertical centering
+ // from the container and size compat bounds.
+ // The container bounds contain the parent bounds offset in the display, for
+ // example when an activity is in the lower split of split screen.
+ final int screenPosY = (mSizeCompatBounds == null
? (containerBounds.height() - resolvedBounds.height()) / 2
- : (containerBounds.height() - mSizeCompatBounds.height()) / 2;
+ : (containerBounds.height() - mSizeCompatBounds.height()) / 2)
+ + containerBounds.top;
+
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
mSizeCompatBounds.offset(screenPosX, screenPosY);
@@ -8015,11 +8149,11 @@
return false;
}
// Never apply sandboxing to an app that should be explicitly excluded from the config.
- if (info != null && info.neverSandboxDisplayApis()) {
+ if (info.neverSandboxDisplayApis(sConstrainDisplayApisConfig)) {
return false;
}
// Always apply sandboxing to an app that should be explicitly included from the config.
- if (info != null && info.alwaysSandboxDisplayApis()) {
+ if (info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig)) {
return true;
}
// Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it
@@ -9035,6 +9169,9 @@
proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
proto.write(MIN_ASPECT_RATIO, getMinAspectRatio());
+ // Only record if max bounds sandboxing is applied, if the caller has the necessary
+ // permission to access the device configs.
+ proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds());
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 6ad2f7c..bb5d962 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -496,11 +496,13 @@
* @param activityIntent intent to start the activity.
* @param activityOptions ActivityOptions to start the activity with.
* @param resultTo the caller activity
+ * @param callingUid the caller uid
+ * @param callingPid the caller pid
* @return the start result.
*/
int startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
@NonNull Intent activityIntent, @Nullable Bundle activityOptions,
- @Nullable IBinder resultTo) {
+ @Nullable IBinder resultTo, int callingUid, int callingPid) {
final ActivityRecord caller =
resultTo != null ? ActivityRecord.forTokenLocked(resultTo) : null;
return obtainStarter(activityIntent, "startActivityInTaskFragment")
@@ -508,8 +510,8 @@
.setInTaskFragment(taskFragment)
.setResultTo(resultTo)
.setRequestCode(-1)
- .setCallingUid(Binder.getCallingUid())
- .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
.setUserId(caller != null ? caller.mUserId : mService.getCurrentUserId())
.execute();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index dc5126d..436a325 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -72,6 +72,8 @@
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;
@@ -1275,7 +1277,7 @@
// This is used to block background activity launch even if the app is still
// visible to user after user clicking home button.
- final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed();
+ 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);
@@ -1288,7 +1290,9 @@
// 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.
- if (((appSwitchAllowed || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
+ final boolean appSwitchAllowedOrFg =
+ appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
+ if (((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
&& callingUidHasAnyVisibleWindow)
|| isCallingUidPersistentSystemProcess) {
if (DEBUG_ACTIVITY_STARTS) {
@@ -1398,7 +1402,7 @@
// don't abort if the callerApp or other processes of that uid are allowed in any way
if (callerApp != null) {
// first check the original calling process
- if (callerApp.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
+ if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
+ callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
@@ -1412,7 +1416,7 @@
for (int i = uidProcesses.size() - 1; i >= 0; i--) {
final WindowProcessController proc = uidProcesses.valueAt(i);
if (proc != callerApp
- && proc.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
+ && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG,
"Background activity start allowed: process " + proc.getPid()
@@ -1426,7 +1430,7 @@
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
- + "; appSwitchAllowed: " + appSwitchAllowed
+ + "; appSwitchState: " + appSwitchState
+ "; isCallingUidForeground: " + isCallingUidForeground
+ "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow
+ "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
@@ -2741,8 +2745,8 @@
// If it exist, we need to reparent target root task from TDA to launch root task.
final TaskDisplayArea tda = mTargetRootTask.getDisplayArea();
final Task launchRootTask = tda.getLaunchRootTask(mTargetRootTask.getWindowingMode(),
- mTargetRootTask.getActivityType(), null /** options */,
- mSourceRootTask, 0 /** launchFlags */);
+ mTargetRootTask.getActivityType(), null /** options */, mSourceRootTask,
+ mLaunchFlags);
// If target root task is created by organizer, let organizer handle reparent itself.
if (!mTargetRootTask.mCreatedByOrganizer && launchRootTask != null
&& launchRootTask != mTargetRootTask) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 60a514e..c8227d9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -504,7 +504,27 @@
* Whether normal application switches are allowed; a call to {@link #stopAppSwitches()
* disables this.
*/
- private volatile boolean mAppSwitchesAllowed = true;
+ private volatile int mAppSwitchesState = APP_SWITCH_ALLOW;
+
+ // The duration of resuming foreground app switch from disallow.
+ private static final long RESUME_FG_APP_SWITCH_MS = 500;
+
+ /** App switch is not allowed. */
+ static final int APP_SWITCH_DISALLOW = 0;
+
+ /** App switch is allowed only if the activity launch was requested by a foreground app. */
+ static final int APP_SWITCH_FG_ONLY = 1;
+
+ /** App switch is allowed. */
+ static final int APP_SWITCH_ALLOW = 2;
+
+ @IntDef({
+ APP_SWITCH_DISALLOW,
+ APP_SWITCH_FG_ONLY,
+ APP_SWITCH_ALLOW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface AppSwitchState {}
/**
* Last stop app switches time, apps finished before this time cannot start background activity
@@ -1247,7 +1267,7 @@
if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null
&& topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid
== Binder.getCallingUid()) {
- mAppSwitchesAllowed = true;
+ mAppSwitchesState = APP_SWITCH_ALLOW;
}
}
return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null,
@@ -2158,8 +2178,8 @@
/**
* Return true if app switching is allowed.
*/
- boolean getBalAppSwitchesAllowed() {
- return mAppSwitchesAllowed;
+ @AppSwitchState int getBalAppSwitchesState() {
+ return mAppSwitchesState;
}
/** Register an {@link AnrController} to control the ANR dialog behavior */
@@ -3680,8 +3700,10 @@
public void stopAppSwitches() {
mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");
synchronized (mGlobalLock) {
- mAppSwitchesAllowed = false;
+ mAppSwitchesState = APP_SWITCH_DISALLOW;
mLastStopAppSwitchesTime = SystemClock.uptimeMillis();
+ mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
+ mH.sendEmptyMessageDelayed(H.RESUME_FG_APP_SWITCH_MSG, RESUME_FG_APP_SWITCH_MS);
}
}
@@ -3689,7 +3711,8 @@
public void resumeAppSwitches() {
mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
synchronized (mGlobalLock) {
- mAppSwitchesAllowed = true;
+ mAppSwitchesState = APP_SWITCH_ALLOW;
+ mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
}
}
@@ -5170,6 +5193,7 @@
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
static final int END_POWER_MODE_UNKNOWN_VISIBILITY_MSG = 3;
+ static final int RESUME_FG_APP_SWITCH_MSG = 4;
static final int FIRST_ACTIVITY_TASK_MSG = 100;
static final int FIRST_SUPERVISOR_TASK_MSG = 200;
@@ -5207,6 +5231,14 @@
}
}
break;
+ case RESUME_FG_APP_SWITCH_MSG: {
+ synchronized (mGlobalLock) {
+ if (mAppSwitchesState == APP_SWITCH_DISALLOW) {
+ mAppSwitchesState = APP_SWITCH_FG_ONLY;
+ }
+ }
+ }
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index b9353e1..421a1c9 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1634,21 +1634,21 @@
}
@TransitionType int getKeyguardTransition() {
- // In case we unocclude Keyguard and occlude it again, meaning that we never actually
- // unoccclude/occlude Keyguard, but just run a normal transition.
- final int occludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_UNOCCLUDE);
- if (occludeIndex != -1
- && occludeIndex < mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_OCCLUDE)) {
+ if (mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_GOING_AWAY) != -1) {
+ return TRANSIT_KEYGUARD_GOING_AWAY;
+ }
+ final int unoccludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_UNOCCLUDE);
+ final int occludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_OCCLUDE);
+ // No keyguard related transition requests.
+ if (unoccludeIndex == -1 && occludeIndex == -1) {
return TRANSIT_NONE;
}
-
- for (int i = 0; i < mNextAppTransitionRequests.size(); ++i) {
- final @TransitionType int transit = mNextAppTransitionRequests.get(i);
- if (isKeyguardTransit(transit)) {
- return transit;
- }
+ // In case we unocclude Keyguard and occlude it again, meaning that we never actually
+ // unoccclude/occlude Keyguard, but just run a normal transition.
+ if (unoccludeIndex != -1 && unoccludeIndex < occludeIndex) {
+ return TRANSIT_NONE;
}
- return TRANSIT_NONE;
+ return unoccludeIndex != -1 ? TRANSIT_KEYGUARD_UNOCCLUDE : TRANSIT_KEYGUARD_OCCLUDE;
}
@TransitionType int getFirstAppTransition() {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index df9a6d2..721907c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -176,6 +176,9 @@
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
+ // TODO(b/205335975): Remove window which stuck in animatingExit status. Find actual cause.
+ mDisplayContent.forAllWindows(WindowState::cleanupAnimatingExitWindow,
+ true /* traverseTopToBottom */);
// TODO(new-app-transition): Remove code using appTransition.getAppTransition()
final AppTransition appTransition = mDisplayContent.mAppTransition;
@@ -590,7 +593,7 @@
}
// We don't want the organizer to handle transition of non-embedded activity of other
// app.
- if (r.getUid() != rootActivity.getUid() && !r.isEmbedded()) {
+ if (r.getUid() != task.effectiveUid && !r.isEmbedded()) {
leafTask = null;
break;
}
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 71a10df..0afd872 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -20,6 +20,8 @@
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.ACTIVITY_BG_START_GRACE_PERIOD_MS;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -70,13 +72,13 @@
}
boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
- boolean appSwitchAllowed, boolean isCheckingForFgsStart,
+ int appSwitchState, boolean isCheckingForFgsStart,
boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
long lastStopAppSwitchesTime, long lastActivityLaunchTime,
long lastActivityFinishTime) {
// If app switching is not allowed, we ignore all the start activity grace period
// exception so apps cannot start itself in onPause() after pressing home button.
- if (appSwitchAllowed) {
+ if (appSwitchState == APP_SWITCH_ALLOW) {
// Allow if any activity in the caller has either started or finished very recently, and
// it must be started or finished after last stop app switches time.
final long now = SystemClock.uptimeMillis();
@@ -111,7 +113,8 @@
return true;
}
// Allow if the caller has an activity in any foreground task.
- if (appSwitchAllowed && hasActivityInVisibleTask) {
+ if (hasActivityInVisibleTask
+ && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid
+ ")] Activity start allowed: process has activity in foreground task");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 07ab571..d1e9d6b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -34,6 +34,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Build.VERSION_CODES.N;
+import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.util.RotationUtils.deltaRotation;
@@ -43,6 +44,8 @@
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static android.view.Display.STATE_UNKNOWN;
+import static android.view.Display.isSuspendedState;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -62,6 +65,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
@@ -320,11 +324,6 @@
*/
private Rect mLastMirroredDisplayAreaBounds = null;
- /**
- * The last state of the display.
- */
- private int mLastDisplayState;
-
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
@@ -592,7 +591,7 @@
/** Caches the value whether told display manager that we have content. */
private boolean mLastHasContent;
- private static DisplayRotationUtil sRotationUtil = new DisplayRotationUtil();
+ private DisplayRotationUtil mRotationUtil = new DisplayRotationUtil();
/**
* The input method window for this display.
@@ -1656,11 +1655,6 @@
// to cover the activity configuration change.
return false;
}
- if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) {
- // Currently it is unknown that when will IME window be ready. Reject the case to
- // avoid flickering by showing IME in inconsistent orientation.
- return false;
- }
if (checkOpening) {
if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
// Apply normal rotation animation in case of the activity set different requested
@@ -2045,16 +2039,14 @@
final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
- final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
- displayCutout);
- final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+ final Point appSize = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode,
displayCutout);
mDisplayInfo.rotation = rotation;
mDisplayInfo.logicalWidth = dw;
mDisplayInfo.logicalHeight = dh;
mDisplayInfo.logicalDensityDpi = mBaseDisplayDensity;
- mDisplayInfo.appWidth = appWidth;
- mDisplayInfo.appHeight = appHeight;
+ mDisplayInfo.appWidth = appSize.x;
+ mDisplayInfo.appHeight = appSize.y;
if (isDefaultDisplay) {
mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
@@ -2096,35 +2088,29 @@
return mDisplayCutoutCache.getOrCompute(mInitialDisplayCutout, rotation);
}
- static WmDisplayCutout calculateDisplayCutoutForRotationAndDisplaySizeUncached(
- DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) {
+ private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
+ DisplayCutout cutout, int rotation) {
if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
return WmDisplayCutout.NO_CUTOUT;
}
if (rotation == ROTATION_0) {
return WmDisplayCutout.computeSafeInsets(
- cutout, displayWidth, displayHeight);
+ cutout, mInitialDisplayWidth, mInitialDisplayHeight);
}
final Insets waterfallInsets =
RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final Rect[] newBounds = sRotationUtil.getRotatedBounds(
+ final Rect[] newBounds = mRotationUtil.getRotatedBounds(
cutout.getBoundingRectsAll(),
- rotation, displayWidth, displayHeight);
+ rotation, mInitialDisplayWidth, mInitialDisplayHeight);
final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
info.getCutoutSpec(), rotation, info.getScale());
return WmDisplayCutout.computeSafeInsets(
DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
- rotated ? displayHeight : displayWidth,
- rotated ? displayWidth : displayHeight);
- }
-
- private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
- DisplayCutout cutout, int rotation) {
- return calculateDisplayCutoutForRotationAndDisplaySizeUncached(cutout, rotation,
- mInitialDisplayWidth, mInitialDisplayHeight);
+ rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
+ rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
RoundedCorners calculateRoundedCornersForRotation(int rotation) {
@@ -2189,24 +2175,22 @@
/** Compute configuration related to application without changing current display. */
private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
int rotation, int uiMode, DisplayCutout displayCutout) {
- final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
- displayCutout);
- final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+ final Point appSize = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode,
displayCutout);
mDisplayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
final int leftInset = mTmpRect.left;
final int topInset = mTmpRect.top;
// AppBounds at the root level should mirror the app screen size.
outConfig.windowConfiguration.setAppBounds(leftInset /* left */, topInset /* top */,
- leftInset + appWidth /* right */, topInset + appHeight /* bottom */);
+ leftInset + appSize.x /* right */, topInset + appSize.y /* bottom */);
outConfig.windowConfiguration.setRotation(rotation);
outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
final float density = mDisplayMetrics.density;
- outConfig.screenWidthDp = (int) (mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation,
- uiMode, displayCutout) / density);
- outConfig.screenHeightDp = (int) (mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation,
- uiMode, displayCutout) / density);
+ final Point configSize = mDisplayPolicy.getConfigDisplaySize(dw, dh, rotation, uiMode,
+ displayCutout);
+ outConfig.screenWidthDp = (int) (configSize.x / density);
+ outConfig.screenHeightDp = (int) (configSize.y / density);
outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);
outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
@@ -2345,10 +2329,10 @@
DisplayMetrics dm, int dw, int dh) {
final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
rotation).getDisplayCutout();
- dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
+ final Point nonDecorSize = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode,
displayCutout);
- dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
- displayCutout);
+ dm.noncompatWidthPixels = nonDecorSize.x;
+ dm.noncompatHeightPixels = nonDecorSize.y;
float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
if (curSize == 0 || size < curSize) {
@@ -2400,12 +2384,12 @@
rotation).getDisplayCutout();
// Get the app screen size at this rotation.
- int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
- int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
+ final Point size = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode,
+ displayCutout);
// Compute the screen layout size class for this rotation.
- int longSize = w;
- int shortSize = h;
+ int longSize = size.x;
+ int shortSize = size.y;
if (longSize < shortSize) {
int tmp = longSize;
longSize = shortSize;
@@ -2420,21 +2404,19 @@
int uiMode, int dw, int dh) {
final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
rotation).getDisplayCutout();
- final int width = mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
+ final Point size = mDisplayPolicy.getConfigDisplaySize(dw, dh, rotation, uiMode,
displayCutout);
- if (width < displayInfo.smallestNominalAppWidth) {
- displayInfo.smallestNominalAppWidth = width;
+ if (size.x < displayInfo.smallestNominalAppWidth) {
+ displayInfo.smallestNominalAppWidth = size.x;
}
- if (width > displayInfo.largestNominalAppWidth) {
- displayInfo.largestNominalAppWidth = width;
+ if (size.x > displayInfo.largestNominalAppWidth) {
+ displayInfo.largestNominalAppWidth = size.x;
}
- final int height = mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
- displayCutout);
- if (height < displayInfo.smallestNominalAppHeight) {
- displayInfo.smallestNominalAppHeight = height;
+ if (size.y < displayInfo.smallestNominalAppHeight) {
+ displayInfo.smallestNominalAppHeight = size.y;
}
- if (height > displayInfo.largestNominalAppHeight) {
- displayInfo.largestNominalAppHeight = height;
+ if (size.y > displayInfo.largestNominalAppHeight) {
+ displayInfo.largestNominalAppHeight = size.y;
}
}
@@ -4142,11 +4124,11 @@
* which controls the visibility and animation of the input method window.
*/
void updateImeInputAndControlTarget(WindowState target) {
+ if (target != null && target.mActivityRecord != null) {
+ target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+ }
if (mImeInputTarget != target) {
ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
- if (target != null && target.mActivityRecord != null) {
- target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
- }
setImeInputTarget(target);
mInsetsStateController.updateAboveInsetsState(mInputMethodWindow, mInsetsStateController
.getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
@@ -4721,12 +4703,9 @@
mWmService.requestTraversal();
}
+ @Override
boolean okToDisplay() {
- return okToDisplay(false);
- }
-
- boolean okToDisplay(boolean ignoreFrozen) {
- return okToDisplay(ignoreFrozen, false /* ignoreScreenOn */);
+ return okToDisplay(false /* ignoreFrozen */, false /* ignoreScreenOn */);
}
boolean okToDisplay(boolean ignoreFrozen, boolean ignoreScreenOn) {
@@ -4738,18 +4717,12 @@
return mDisplayInfo.state == Display.STATE_ON;
}
- boolean okToAnimate() {
- return okToAnimate(false);
- }
-
- boolean okToAnimate(boolean ignoreFrozen) {
- return okToAnimate(ignoreFrozen, false /* ignoreScreenOn */);
- }
-
+ @Override
boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
return okToDisplay(ignoreFrozen, ignoreScreenOn)
&& (mDisplayId != DEFAULT_DISPLAY
- || mWmService.mPolicy.okToAnimate(ignoreScreenOn));
+ || mWmService.mPolicy.okToAnimate(ignoreScreenOn))
+ && getDisplayPolicy().isScreenOnFully();
}
static final class TaskForResizePointSearchResult {
@@ -4882,7 +4855,10 @@
// WindowState#applyImeWindowsIfNeeded} in case of any state mismatch.
return dc.mImeLayeringTarget != null
&& (!dc.getDefaultTaskDisplayArea().isSplitScreenModeActivated()
- || dc.mImeLayeringTarget.getTask() == null);
+ || dc.mImeLayeringTarget.getTask() == null)
+ // Make sure that the IME window won't be skipped to report that it has
+ // completed the orientation change.
+ && !dc.mWmService.mDisplayFrozen;
}
/** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -4997,6 +4973,12 @@
reconfigureDisplayLocked();
onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
+ // Attach the SystemUiContext to this DisplayContent the get latest configuration.
+ // Note that the SystemUiContext will be removed automatically if this DisplayContent
+ // is detached.
+ mWmService.mWindowContextListenerController.registerWindowContainerListener(
+ getDisplayUiContext().getWindowContextToken(), this, SYSTEM_UID,
+ INVALID_WINDOW_TYPE, null /* options */);
}
}
@@ -5615,12 +5597,13 @@
void onDisplayChanged() {
mDisplay.getRealSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+ final int lastDisplayState = mDisplayInfo.state;
updateDisplayInfo();
// The window policy is responsible for stopping activities on the default display.
final int displayId = mDisplay.getDisplayId();
+ final int displayState = mDisplayInfo.state;
if (displayId != DEFAULT_DISPLAY) {
- final int displayState = mDisplay.getState();
if (displayState == Display.STATE_OFF) {
mOffTokenAcquirer.acquire(mDisplayId);
} else if (displayState == Display.STATE_ON) {
@@ -5629,12 +5612,18 @@
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
"Display %d state is now (%d), so update layer mirroring?",
mDisplayId, displayState);
- if (mLastDisplayState != displayState) {
+ if (lastDisplayState != displayState) {
// If state is on due to surface being added, then start layer mirroring.
// If state is off due to surface being removed, then stop layer mirroring.
updateMirroring();
}
- mLastDisplayState = displayState;
+ }
+ // Dispatch pending Configuration to WindowContext if the associated display changes to
+ // un-suspended state from suspended.
+ if (isSuspendedState(lastDisplayState)
+ && !isSuspendedState(displayState) && displayState != STATE_UNKNOWN) {
+ mWmService.mWindowContextListenerController
+ .dispatchPendingConfigurationIfNeeded(mDisplayId);
}
mWmService.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index eb82957..d5d206a 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -54,7 +54,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
@@ -121,6 +120,7 @@
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;
@@ -452,7 +452,7 @@
: service.mContext.createDisplayContext(displayContent.getDisplay());
mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext
: service.mAtmService.mSystemThread
- .createSystemUiContext(displayContent.getDisplayId());
+ .getSystemUiContext(displayContent.getDisplayId());
mDisplayContent = displayContent;
mLock = service.getWindowManagerLock();
@@ -882,20 +882,6 @@
}
/**
- * Only trusted overlays are allowed to use FLAG_SLIPPERY.
- */
- static int sanitizeFlagSlippery(int flags, int privateFlags, String name) {
- if ((flags & FLAG_SLIPPERY) == 0) {
- return flags;
- }
- if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
- return flags;
- }
- Slog.w(TAG, "Removing FLAG_SLIPPERY for non-trusted overlay " + name);
- return flags & ~FLAG_SLIPPERY;
- }
-
- /**
* Sanitize the layout parameters coming from a client. Allows the policy
* to do things like ensure that windows of a specific type can't take
* input focus.
@@ -966,8 +952,6 @@
if (mExtraNavBarAlt == win) {
mExtraNavBarAltPosition = getAltBarPosition(attrs);
}
-
- attrs.flags = sanitizeFlagSlippery(attrs.flags, attrs.privateFlags, win.getName());
}
/**
@@ -2260,7 +2244,8 @@
// user's package info (see ContextImpl.createDisplayContext)
final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo(
uiContext.getPackageName(), null, 0, userId);
- mCurrentUserResources = ResourcesManager.getInstance().getResources(null,
+ mCurrentUserResources = ResourcesManager.getInstance().getResources(
+ uiContext.getWindowContextToken(),
pi.getResDir(),
null /* splitResDirs */,
pi.getOverlayDirs(),
@@ -2324,6 +2309,24 @@
}
}
+ private int getAltBarWidth(@InternalInsetsType int insetsType) {
+ final InsetsSource source = mDisplayContent.getInsetsStateController().getRawInsetsState()
+ .peekSource(insetsType);
+ if (source == null) {
+ return 0;
+ }
+ return source.getFrame().width();
+ }
+
+ private int getAltBarHeight(@InternalInsetsType int insetsType) {
+ final InsetsSource source = mDisplayContent.getInsetsStateController().getRawInsetsState()
+ .peekSource(insetsType);
+ if (source == null) {
+ return 0;
+ }
+ return source.getFrame().height();
+ }
+
void notifyDisplayReady() {
mHandler.post(() -> {
final int displayId = getDisplayId();
@@ -2339,26 +2342,6 @@
});
}
- /**
- * Return the display width available after excluding any screen
- * decorations that could never be removed in Honeycomb. That is, system bar or
- * button bar.
- */
- public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
- DisplayCutout displayCutout) {
- int width = fullWidth;
- if (hasNavigationBar()) {
- final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
- if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
- width -= getNavigationBarWidth(rotation, uiMode, navBarPosition);
- }
- }
- if (displayCutout != null) {
- width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
- }
- return width;
- }
-
private int getNavigationBarHeight(int rotation, int uiMode) {
if (INSETS_LAYOUT_GENERALIZATION) {
if (mNavigationBar == null) {
@@ -2406,43 +2389,59 @@
}
/**
- * Return the display height available after excluding any screen
+ * Return the display size available after excluding any screen
* decorations that could never be removed in Honeycomb. That is, system bar or
* button bar.
*/
- public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
+ Point getNonDecorDisplaySize(int fullWidth, int fullHeight, int rotation, int uiMode,
DisplayCutout displayCutout) {
+ int width = fullWidth;
int height = fullHeight;
+ int navBarReducedHeight = 0;
+ int navBarReducedWidth = 0;
+ final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
if (hasNavigationBar()) {
- final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
if (navBarPosition == NAV_BAR_BOTTOM) {
- height -= getNavigationBarHeight(rotation, uiMode);
+ navBarReducedHeight = getNavigationBarHeight(rotation, uiMode);
+ } else if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
+ navBarReducedWidth = getNavigationBarWidth(rotation, uiMode, navBarPosition);
}
}
+ if (mExtraNavBarAlt != null) {
+ final LayoutParams altBarParams = mExtraNavBarAlt.getLayoutingAttrs(rotation);
+ final int altBarPosition = getAltBarPosition(altBarParams);
+ if (altBarPosition == ALT_BAR_BOTTOM || altBarPosition == ALT_BAR_TOP) {
+ if (altBarPosition == navBarPosition) {
+ navBarReducedHeight = Math.max(navBarReducedHeight,
+ getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));
+ } else {
+ navBarReducedHeight += getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR);
+ }
+ } else if (altBarPosition == ALT_BAR_LEFT || altBarPosition == ALT_BAR_RIGHT) {
+ if (altBarPosition == navBarPosition) {
+ navBarReducedWidth = Math.max(navBarReducedWidth,
+ getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));
+ } else {
+ navBarReducedWidth += getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR);
+ }
+ }
+ }
+ height -= navBarReducedHeight;
+ width -= navBarReducedWidth;
if (displayCutout != null) {
height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
+ width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
}
- return height;
+ return new Point(width, height);
}
/**
- * Return the available screen width that we should report for the
+ * Return the available screen size that we should report for the
* configuration. This must be no larger than
- * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller
+ * {@link #getNonDecorDisplaySize(int, int, int, int, DisplayCutout)}; it may be smaller
* than that to account for more transient decoration like a status bar.
*/
- public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
- DisplayCutout displayCutout) {
- return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout);
- }
-
- /**
- * Return the available screen height that we should report for the
- * configuration. This must be no larger than
- * {@link #getNonDecorDisplayHeight(int, int, int, int, DisplayCutout)}; it may be smaller
- * than that to account for more transient decoration like a status bar.
- */
- public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
+ Point getConfigDisplaySize(int fullWidth, int fullHeight, int rotation, int uiMode,
DisplayCutout displayCutout) {
// There is a separate status bar at the top of the display. We don't count that as part
// of the fixed decor, since it can hide; however, for purposes of configurations,
@@ -2454,8 +2453,9 @@
// bar height.
statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
}
- return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayCutout)
- - statusBarHeight;
+ final Point nonDecorSize = getNonDecorDisplaySize(fullWidth, fullHeight, rotation,
+ uiMode, displayCutout);
+ return new Point(nonDecorSize.x, nonDecorSize.y - statusBarHeight);
}
/**
@@ -2503,7 +2503,7 @@
/**
* Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
- * bar or button bar. See {@link #getNonDecorDisplayWidth}.
+ * bar or button bar. See {@link #getNonDecorDisplaySize}.
*
* @param displayRotation the current display rotation
* @param displayWidth the current display width
@@ -2515,7 +2515,7 @@
DisplayCutout displayCutout, Rect outInsets) {
outInsets.setEmpty();
- // Only navigation bar
+ // Only navigation bar and extra navigation bar
if (hasNavigationBar()) {
final int uiMode = mService.mPolicy.getUiMode();
int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
@@ -2527,6 +2527,24 @@
outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position);
}
}
+ if (mExtraNavBarAlt != null) {
+ final LayoutParams extraNavLayoutParams =
+ mExtraNavBarAlt.getLayoutingAttrs(displayRotation);
+ final int position = getAltBarPosition(extraNavLayoutParams);
+ if (position == ALT_BAR_BOTTOM) {
+ outInsets.bottom = Math.max(outInsets.bottom,
+ getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));
+ } else if (position == ALT_BAR_RIGHT) {
+ outInsets.right = Math.max(outInsets.right,
+ getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));
+ } else if (position == ALT_BAR_LEFT) {
+ outInsets.left = Math.max(outInsets.left,
+ getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));
+ } else if (position == ALT_BAR_TOP) {
+ outInsets.top = Math.max(outInsets.top,
+ getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));
+ }
+ }
if (displayCutout != null) {
outInsets.left += displayCutout.getSafeInsetLeft();
@@ -2818,8 +2836,7 @@
@VisibleForTesting
int updateLightNavigationBarLw(int appearance, WindowState navColorWin) {
- if (navColorWin == null || navColorWin.isDimming()
- || !isLightBarAllowed(navColorWin, Type.navigationBars())) {
+ if (navColorWin == null || !isLightBarAllowed(navColorWin, Type.navigationBars())) {
// Clear the light flag while not allowed.
appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
return appearance;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 1e7b676..6afd335 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -51,7 +51,6 @@
import static java.lang.Integer.MAX_VALUE;
import android.annotation.Nullable;
-import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
import android.os.IBinder;
@@ -303,9 +302,6 @@
&& !mDisableWallpaperTouchEvents;
inputWindowHandle.setHasWallpaper(hasWallpaper);
- final Rect frame = w.getFrame();
- inputWindowHandle.setFrame(frame.left, frame.top, frame.right, frame.bottom);
-
// Surface insets are hardcoded to be the same in all directions
// and we could probably deprecate the "left/right/top/bottom" concept.
// we avoid reintroducing this concept by just choosing one of them here.
@@ -315,11 +311,19 @@
// what is on screen to what is actually being touched in the UI.
inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f);
- final int flags = w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs.flags);
- inputWindowHandle.setTouchableRegion(mTmpRegion);
+ // Update layout params flags to force the window to be not touch modal. We do this to
+ // restrict the window's touchable region to the task even if it request touches outside its
+ // window bounds. An example is a dialog in primary split should get touches outside its
+ // window within the primary task but should not get any touches going to the secondary
+ // task.
+ int flags = w.mAttrs.flags;
+ if (w.mAttrs.isModal()) {
+ flags = flags | FLAG_NOT_TOUCH_MODAL;
+ }
inputWindowHandle.setLayoutParamsFlags(flags);
- boolean useSurfaceCrop = false;
+ boolean useSurfaceBoundsAsTouchRegion = false;
+ SurfaceControl touchableRegionCrop = null;
final Task task = w.getTask();
if (task != null) {
// TODO(b/165794636): Remove the special case for freeform window once drag resizing is
@@ -331,20 +335,22 @@
// we need to make sure that these changes in crop are reflected in the input
// windows, and so ensure this flag is set so that the input crop always reflects
// the surface hierarchy.
- // TODO(b/168252846): we have some issues with modal-windows, so we need to cross
- // that bridge now that we organize full-screen Tasks.
- inputWindowHandle.setTouchableRegionCrop(null /* Use this surfaces crop */);
- inputWindowHandle.setReplaceTouchableRegionWithCrop(true);
- useSurfaceCrop = true;
+ useSurfaceBoundsAsTouchRegion = true;
+
+ if (w.mAttrs.isModal()) {
+ TaskFragment parent = w.getTaskFragment();
+ touchableRegionCrop = parent != null ? parent.getSurfaceControl() : null;
+ }
} else if (task.cropWindowsToRootTaskBounds() && !w.inFreeformWindowingMode()) {
- inputWindowHandle.setTouchableRegionCrop(task.getRootTask().getSurfaceControl());
- inputWindowHandle.setReplaceTouchableRegionWithCrop(false);
- useSurfaceCrop = true;
+ touchableRegionCrop = task.getRootTask().getSurfaceControl();
}
}
- if (!useSurfaceCrop) {
- inputWindowHandle.setReplaceTouchableRegionWithCrop(false);
- inputWindowHandle.setTouchableRegionCrop(null);
+ inputWindowHandle.setReplaceTouchableRegionWithCrop(useSurfaceBoundsAsTouchRegion);
+ inputWindowHandle.setTouchableRegionCrop(touchableRegionCrop);
+
+ if (!useSurfaceBoundsAsTouchRegion) {
+ w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs);
+ inputWindowHandle.setTouchableRegion(mTmpRegion);
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index cbb473c..212a036 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -30,8 +30,7 @@
final class LetterboxConfiguration {
/**
- * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
- * set-fixed-orientation-letterbox-aspect-ratio or via {@link
+ * Override of aspect ratio for fixed orientation letterboxing that is set via {@link
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
* if it is <= this value.
*/
@@ -251,7 +250,7 @@
/**
* Gets {@link LetterboxBackgroundType} specified in {@link
- * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
+ * com.android.internal.R.integer.config_letterboxBackgroundType}.
*/
@LetterboxBackgroundType
int getLetterboxBackgroundType() {
@@ -359,9 +358,8 @@
/*
* Gets horizontal position of a center of the letterboxed app window specified
- * in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}
- * or via an ADB command. 0 corresponds to the left side of the screen and 1 to the
- * right side.
+ * in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
+ * 0 corresponds to the left side of the screen and 1 to the right side.
*/
float getLetterboxHorizontalPositionMultiplier() {
return (mLetterboxHorizontalPositionMultiplier < 0.0f
@@ -416,8 +414,7 @@
/*
* Gets default horizontal position of the letterboxed app window when reachability is enabled.
- * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability} or via an ADB
- * command.
+ * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability}.
*/
@LetterboxReachabilityPosition
int getDefaultPositionForReachability() {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 94a175c..8a2d116 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -251,15 +251,47 @@
*/
boolean activityBlockedFromFinish(ActivityRecord activity) {
final Task task = activity.getTask();
- if (activity == task.getRootActivity()
- && activity == task.getTopNonFinishingActivity()
- && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
- && isRootTask(task)) {
- Slog.i(TAG, "Not finishing task in lock task mode");
- showLockTaskToast();
- return true;
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV || !isRootTask(task)) {
+ return false;
}
- return false;
+
+ final ActivityRecord taskTop = task.getTopNonFinishingActivity();
+ final ActivityRecord taskRoot = task.getRootActivity();
+ // If task has more than one Activity, verify if there's only adjacent TaskFragments that
+ // should be finish together in the Task.
+ if (activity != taskRoot || activity != taskTop) {
+ final TaskFragment taskFragment = activity.getTaskFragment();
+ final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+ if (taskFragment.asTask() != null
+ || !taskFragment.isDelayLastActivityRemoval()
+ || adjacentTaskFragment == null) {
+ // Don't block activity from finishing if the TaskFragment don't have any adjacent
+ // TaskFragment, or it won't finish together with its adjacent TaskFragment.
+ return false;
+ }
+
+ final boolean hasOtherActivityInTaskFragment =
+ taskFragment.getActivity(a -> !a.finishing && a != activity) != null;
+ if (hasOtherActivityInTaskFragment) {
+ // Don't block activity from finishing if there's other Activity in the same
+ // TaskFragment.
+ return false;
+ }
+
+ final boolean hasOtherActivityInTask = task.getActivity(a -> !a.finishing
+ && a != activity && a.getTaskFragment() != adjacentTaskFragment) != null;
+ if (hasOtherActivityInTask) {
+ // Do not block activity from finishing if there are another running activities
+ // after the current and adjacent TaskFragments are removed. Note that we don't
+ // check activities in adjacent TaskFragment because it will be finished together
+ // with TaskFragment regardless of numbers of activities.
+ return false;
+ }
+ }
+
+ Slog.i(TAG, "Not finishing task in lock task mode");
+ showLockTaskToast();
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index b54208d..1da0fe7 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -243,7 +243,8 @@
int oldRotation, int newRotation) {
final Rect bounds = mDestRotatedBounds;
final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
- if (bounds == null && pipTx == null) {
+ final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
+ if (bounds == null && emptyPipPositionTx) {
return;
}
final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
@@ -255,25 +256,27 @@
mDestRotatedBounds = null;
mPipTransaction = null;
final Rect areaBounds = taskArea.getBounds();
- if (pipTx != null) {
+ if (!emptyPipPositionTx) {
// The transaction from recents animation is in old rotation. So the position needs to
// be rotated.
- float dx = pipTx.mPositionX;
- float dy = pipTx.mPositionY;
+ float dx = pipTx.mPosition.x;
+ float dy = pipTx.mPosition.y;
final Matrix matrix = pipTx.getMatrix();
if (pipTx.mRotation == 90) {
- dx = pipTx.mPositionY;
- dy = areaBounds.right - pipTx.mPositionX;
+ dx = pipTx.mPosition.y;
+ dy = areaBounds.right - pipTx.mPosition.x;
matrix.postRotate(-90);
} else if (pipTx.mRotation == -90) {
- dx = areaBounds.bottom - pipTx.mPositionY;
- dy = pipTx.mPositionX;
+ dx = areaBounds.bottom - pipTx.mPosition.y;
+ dy = pipTx.mPosition.x;
matrix.postRotate(90);
}
matrix.postTranslate(dx, dy);
final SurfaceControl leash = pinnedTask.getSurfaceControl();
- t.setMatrix(leash, matrix, new float[9])
- .setCornerRadius(leash, pipTx.mCornerRadius);
+ t.setMatrix(leash, matrix, new float[9]);
+ if (pipTx.hasCornerRadiusSet()) {
+ t.setCornerRadius(leash, pipTx.mCornerRadius);
+ }
Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
return;
}
diff --git a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
deleted file mode 100644
index 11a27c5..0000000
--- a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-
-import android.hardware.display.DisplayManagerInternal;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.DisplayInfo;
-import android.view.Surface;
-
-import java.util.Set;
-
-/**
- * Maintains a map of possible {@link DisplayInfo} for displays and states that may be encountered
- * on a device. This is not guaranteed to include all possible device states for all displays.
- *
- * By 'possible', this class only handles device states for displays and display groups it is
- * currently aware of. It can not handle all eventual states the system may enter, for example, if
- * an external display is added, or a new display is added to the group.
- */
-public class PossibleDisplayInfoMapper {
- private static final String TAG = "PossibleDisplayInfoMapper";
- private static final boolean DEBUG = false;
-
- private final DisplayManagerInternal mDisplayManagerInternal;
-
- /**
- * Map of all logical displays, indexed by logical display id.
- * Each logical display has multiple entries, one for each possible rotation and device
- * state.
- *
- * Emptied and re-calculated when a display is added, removed, or changed.
- */
- private final SparseArray<Set<DisplayInfo>> mDisplayInfos = new SparseArray<>();
-
- PossibleDisplayInfoMapper(DisplayManagerInternal displayManagerInternal) {
- mDisplayManagerInternal = displayManagerInternal;
- }
-
-
- /**
- * Returns, for the given displayId, a set of display infos. Set contains the possible rotations
- * for each supported device state.
- */
- public Set<DisplayInfo> getPossibleDisplayInfos(int displayId) {
- // Update display infos before returning, since any cached values would have been removed
- // in response to any display event. This model avoids re-computing the cache for every
- // display change event (which occurs extremely frequently in the normal usage of the
- // device).
- updatePossibleDisplayInfos(displayId);
- if (!mDisplayInfos.contains(displayId)) {
- return new ArraySet<>();
- }
- return Set.copyOf(mDisplayInfos.get(displayId));
- }
-
- /**
- * Updates the possible {@link DisplayInfo}s for the given display, by calculating the
- * DisplayInfo for each rotation across supported device states.
- */
- public void updatePossibleDisplayInfos(int displayId) {
- Set<DisplayInfo> displayInfos = mDisplayManagerInternal.getPossibleDisplayInfo(displayId);
- if (DEBUG) {
- Slog.v(TAG, "updatePossibleDisplayInfos, calculate rotations for given DisplayInfo "
- + displayInfos.size() + " on display " + displayId);
- }
- updateDisplayInfos(displayInfos);
- }
-
- /**
- * For the given displayId, removes all possible {@link DisplayInfo}.
- */
- public void removePossibleDisplayInfos(int displayId) {
- if (DEBUG && mDisplayInfos.get(displayId) != null) {
- Slog.v(TAG, "onDisplayRemoved, remove all DisplayInfo (" + mDisplayInfos.get(
- displayId).size() + ") with id " + displayId);
- }
- mDisplayInfos.remove(displayId);
- }
-
- private void updateDisplayInfos(Set<DisplayInfo> displayInfos) {
- // Empty out cache before re-computing.
- mDisplayInfos.clear();
- DisplayInfo[] originalDisplayInfos = new DisplayInfo[displayInfos.size()];
- displayInfos.toArray(originalDisplayInfos);
- // Iterate over each logical display layout for the current state.
- Set<DisplayInfo> rotatedDisplayInfos;
- for (DisplayInfo di : originalDisplayInfos) {
- rotatedDisplayInfos = new ArraySet<>();
- // Calculate all possible rotations for each logical display.
- for (int rotation = ROTATION_0; rotation <= ROTATION_270; rotation++) {
- rotatedDisplayInfos.add(applyRotation(di, rotation));
- }
- // Combine all results under the logical display id.
- Set<DisplayInfo> priorDisplayInfos = mDisplayInfos.get(di.displayId, new ArraySet<>());
- priorDisplayInfos.addAll(rotatedDisplayInfos);
- mDisplayInfos.put(di.displayId, priorDisplayInfos);
- }
- }
-
- private static DisplayInfo applyRotation(DisplayInfo displayInfo,
- @Surface.Rotation int rotation) {
- DisplayInfo updatedDisplayInfo = new DisplayInfo();
- updatedDisplayInfo.copyFrom(displayInfo);
- // Apply rotations before updating width and height
- updatedDisplayInfo.roundedCorners = updatedDisplayInfo.roundedCorners.rotate(rotation,
- updatedDisplayInfo.logicalWidth, updatedDisplayInfo.logicalHeight);
- updatedDisplayInfo.displayCutout =
- DisplayContent.calculateDisplayCutoutForRotationAndDisplaySizeUncached(
- updatedDisplayInfo.displayCutout, rotation, updatedDisplayInfo.logicalWidth,
- updatedDisplayInfo.logicalHeight).getDisplayCutout();
-
- updatedDisplayInfo.rotation = rotation;
- final int naturalWidth = updatedDisplayInfo.getNaturalWidth();
- final int naturalHeight = updatedDisplayInfo.getNaturalHeight();
- updatedDisplayInfo.logicalWidth = naturalWidth;
- updatedDisplayInfo.logicalHeight = naturalHeight;
- return updatedDisplayInfo;
- }
-}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index fd4b63e..5dd8ef3 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -264,13 +264,6 @@
"finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
final long token = Binder.clearCallingIdentity();
try {
- synchronized (mService.getWindowManagerLock()) {
- // Remove all new task targets.
- for (int i = mPendingNewTaskTargets.size() - 1; i >= 0; i--) {
- removeTaskInternal(mPendingNewTaskTargets.get(i));
- }
- }
-
// Note, the callback will handle its own synchronization, do not lock on WM lock
// prior to calling the callback
mCallbacks.onAnimationFinished(moveHomeToTop
@@ -760,7 +753,7 @@
// the task-id with the leaf id.
final Task leafTask = task.getTopLeafTask();
int taskId = leafTask.mTaskId;
- TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task,
+ TaskAnimationAdapter adapter = addAnimation(task,
!recentTaskIds.get(taskId), true /* hidden */, finishedCallback);
mPendingNewTaskTargets.add(taskId);
return adapter.createRemoteAnimationTarget(taskId);
@@ -1013,6 +1006,7 @@
taskAdapter.onCleanup();
}
// Should already be empty, but clean-up pending task-appears in-case they weren't sent.
+ mPendingNewTaskTargets.clear();
mPendingTaskAppears.clear();
for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b1eca9d..fbc8f73 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2585,11 +2585,8 @@
// starts. Instead, we expect home activities to be launched when the system is ready
// (ActivityManagerService#systemReady).
if (mService.isBooted() || mService.isBooting()) {
- startSystemDecorations(display.mDisplayContent);
+ startSystemDecorations(display);
}
- // Drop any cached DisplayInfos associated with this display id - the values are now
- // out of date given this display added event.
- mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
}
}
@@ -2610,8 +2607,8 @@
if (displayContent == null) {
return;
}
+
displayContent.remove();
- mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
}
}
@@ -2623,9 +2620,6 @@
if (displayContent != null) {
displayContent.onDisplayChanged();
}
- // Drop any cached DisplayInfos associated with this display id - the values are now
- // out of date given this display changed event.
- mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
}
}
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 7ba772c..9864297 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -54,6 +54,7 @@
private boolean mAllowed;
private boolean mFilterOnlyVisibleRecents;
private Task mTopDisplayFocusRootTask;
+ private Task mTopDisplayAdjacentTask;
private RecentTasks mRecentTasks;
private boolean mKeepIntentExtra;
@@ -77,6 +78,12 @@
mRecentTasks = root.mService.getRecentTasks();
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
+ if (mTopDisplayFocusRootTask.getAdjacentTaskFragment() != null) {
+ mTopDisplayAdjacentTask = mTopDisplayFocusRootTask.getAdjacentTaskFragment().asTask();
+ } else {
+ mTopDisplayAdjacentTask = null;
+ }
+
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
PooledLambda.__(Task.class));
root.forAllLeafTasks(c, false);
@@ -126,6 +133,12 @@
// can be used to determine the order of the tasks (it may not be set for newly
// created tasks)
task.touchActiveTime();
+ } else if (rootTask == mTopDisplayAdjacentTask && rootTask.getTopMostTask() == task) {
+ // The short-term workaround for launcher could get suitable running task info in
+ // split screen.
+ task.touchActiveTime();
+ // TreeSet doesn't allow same value and make sure this task is lower than focus one.
+ task.lastActiveTime--;
}
mTmpSortedSet.add(task);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1b7a012..a8032f4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -321,6 +321,11 @@
*/
boolean mInResumeTopActivity = false;
+ /**
+ * Used to identify if the activity that is installed from device's system image.
+ */
+ boolean mIsEffectivelySystemApp;
+
int mCurrentUser;
String affinity; // The affinity name for this task, or null; may change identity.
@@ -568,13 +573,24 @@
if (r.finishing) return false;
- // Set this as the candidate root since it isn't finishing.
- mRoot = r;
+ if (mRoot == null || mRoot.finishing) {
+ // Set this as the candidate root since it isn't finishing.
+ mRoot = r;
+ }
- // Only end search if we are ignore relinquishing identity or we are not relinquishing.
- return ignoreRelinquishIdentity
- || mNeverRelinquishIdentity
- || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+ final int uid = mRoot == r ? effectiveUid : r.info.applicationInfo.uid;
+ if (ignoreRelinquishIdentity
+ || (mRoot.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0
+ || (mRoot.info.applicationInfo.uid != Process.SYSTEM_UID
+ && !mRoot.info.applicationInfo.isSystemApp()
+ && mRoot.info.applicationInfo.uid != uid)) {
+ // No need to relinquish identity, end search.
+ return true;
+ }
+
+ // Relinquish to next activity
+ mRoot = r;
+ return false;
}
}
@@ -999,7 +1015,15 @@
* @param info The activity info which could be different from {@code r.info} if set.
*/
void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) {
- if (this.intent == null || !mNeverRelinquishIdentity) {
+ boolean updateIdentity = false;
+ if (this.intent == null) {
+ updateIdentity = true;
+ } else if (!mNeverRelinquishIdentity) {
+ final ActivityInfo activityInfo = info != null ? info : r.info;
+ updateIdentity = (effectiveUid == Process.SYSTEM_UID || mIsEffectivelySystemApp
+ || effectiveUid == activityInfo.applicationInfo.uid);
+ }
+ if (updateIdentity) {
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
mCallingFeatureId = r.launchedFromFeatureId;
@@ -1012,14 +1036,7 @@
private void setIntent(Intent _intent, ActivityInfo info) {
if (!isLeafTask()) return;
- if (info.applicationInfo.uid == Process.SYSTEM_UID
- || info.applicationInfo.isSystemApp()) {
- // Only allow the apps that pre-installed on the system image to apply
- // relinquishTaskIdentity
- mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
- } else {
- mNeverRelinquishIdentity = true;
- }
+ mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
affinity = info.taskAffinity;
if (intent == null) {
// If this task already has an intent associated with it, don't set the root
@@ -1028,6 +1045,7 @@
rootAffinity = affinity;
}
effectiveUid = info.applicationInfo.uid;
+ mIsEffectivelySystemApp = info.applicationInfo.isSystemApp();
stringName = null;
if (info.targetActivity == null) {
@@ -1451,11 +1469,11 @@
}
/** Called when an {@link ActivityRecord} is added as a descendant */
- void onDescendantActivityAdded(boolean hadChild, int activityType, ActivityRecord r) {
+ void onDescendantActivityAdded(boolean hadActivity, int activityType, ActivityRecord r) {
warnForNonLeafTask("onDescendantActivityAdded");
// Only set this based on the first activity
- if (!hadChild) {
+ if (!hadActivity) {
if (r.getActivityType() == ACTIVITY_TYPE_UNDEFINED) {
// Normally non-standard activity type for the activity record will be set when the
// object is created, however we delay setting the standard application type until
@@ -2163,10 +2181,7 @@
}
private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
- if (mWmService.mDisableTransitionAnimation
- || !isVisible()
- || getSurfaceControl() == null
- || !isLeafTask()) {
+ if (!isLeafTask() || !canStartChangeTransition()) {
return false;
}
// Only do an animation into and out-of freeform mode for now. Other mode
@@ -3144,14 +3159,6 @@
}
@Override
- boolean fillsParent() {
- // From the perspective of policy, we still want to report that this task fills parent
- // in fullscreen windowing mode even it doesn't match parent bounds because there will be
- // letterbox around its real content.
- return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
- }
-
- @Override
void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
final int count = mChildren.size();
boolean isLeafTask = true;
@@ -3453,11 +3460,18 @@
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
+
+ boolean isTopActivityResumed = mReuseActivitiesReport.top != null
+ && mReuseActivitiesReport.top.getOrganizedTask() == this
+ && mReuseActivitiesReport.top.isState(RESUMED);
// Whether the direct top activity is in size compat mode on foreground.
- info.topActivityInSizeCompat = mReuseActivitiesReport.top != null
- && mReuseActivitiesReport.top.getOrganizedTask() == this
- && mReuseActivitiesReport.top.inSizeCompatMode()
- && mReuseActivitiesReport.top.isState(RESUMED);
+ info.topActivityInSizeCompat = isTopActivityResumed
+ && mReuseActivitiesReport.top.inSizeCompatMode();
+ // Whether the direct top activity requested showing camera compat control.
+ info.cameraCompatControlState = isTopActivityResumed
+ ? mReuseActivitiesReport.top.getCameraCompatControlState()
+ : TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+
info.launchCookies.clear();
info.addLaunchCookie(mLaunchCookie);
forAllActivities(r -> {
@@ -4584,14 +4598,15 @@
}
super.setWindowingMode(windowingMode);
- // Try reparent pinned activity back to its original task after onConfigurationChanged
- // cascade finishes. This is done on Task level instead of
- // {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit PiP,
- // we set final windowing mode on the ActivityRecord first and then on its Task when
- // the exit PiP transition finishes. Meanwhile, the exit transition is always
- // performed on its original task, reparent immediately in ActivityRecord breaks it.
- if (currentMode == WINDOWING_MODE_PINNED) {
- if (topActivity != null && topActivity.getLastParentBeforePip() != null) {
+ if (currentMode == WINDOWING_MODE_PINNED && topActivity != null) {
+ // Try reparent pinned activity back to its original task after
+ // onConfigurationChanged cascade finishes. This is done on Task level instead of
+ // {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit
+ // PiP, we set final windowing mode on the ActivityRecord first and then on its
+ // Task when the exit PiP transition finishes. Meanwhile, the exit transition is
+ // always performed on its original task, reparent immediately in ActivityRecord
+ // breaks it.
+ if (topActivity.getLastParentBeforePip() != null) {
// Do not reparent if the pinned task is in removal, indicated by the
// force hidden flag.
if (!isForceHidden()) {
@@ -4604,6 +4619,11 @@
}
}
}
+ // Resume app-switches-allowed flag when exiting from pinned mode since
+ // it does not follow the ActivityStarter path.
+ if (topActivity.shouldBeVisible()) {
+ mAtmService.resumeAppSwitches();
+ }
}
if (creating) {
@@ -4650,23 +4670,14 @@
moveToFront(reason, null);
}
- /**
- * @param reason The reason for moving the root task to the front.
- * @param task If non-null, the task will be moved to the top of the root task.
- */
void moveToFront(String reason, Task task) {
- if (!isAttached()) {
- return;
- }
-
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
-
if (inSplitScreenSecondaryWindowingMode()) {
// If the root task is in split-screen secondary mode, we need to make sure we move the
// primary split-screen root task forward in the case it is currently behind a
// fullscreen root task so both halves of the split-screen appear on-top and the
// fullscreen root task isn't cutting between them.
// TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
final Task topFullScreenRootTask =
taskDisplayArea.getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
if (topFullScreenRootTask != null) {
@@ -4674,10 +4685,30 @@
taskDisplayArea.getRootSplitScreenPrimaryTask();
if (primarySplitScreenRootTask != null
&& topFullScreenRootTask.compareTo(primarySplitScreenRootTask) > 0) {
- primarySplitScreenRootTask.moveToFront(reason + " splitScreenToTop");
+ primarySplitScreenRootTask.moveToFrontInner(reason + " splitScreenToTop",
+ null /* task */);
}
}
+ } else if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) {
+ final Task adjacentTask = getAdjacentTaskFragment().asTask();
+ if (adjacentTask != null) {
+ adjacentTask.moveToFrontInner(reason + " adjacentTaskToTop", null /* task */);
+ }
}
+ moveToFrontInner(reason, task);
+ }
+
+ /**
+ * @param reason The reason for moving the root task to the front.
+ * @param task If non-null, the task will be moved to the top of the root task.
+ */
+ @VisibleForTesting
+ void moveToFrontInner(String reason, Task task) {
+ if (!isAttached()) {
+ return;
+ }
+
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
if (!isActivityTypeHome() && returnsToHomeRootTask()) {
// Make sure the root home task is behind this root task since that is where we
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 1e367dc..ec3554a 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -867,6 +867,10 @@
// Place root home tasks to the bottom.
layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer);
layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer);
+ // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
+ // make app pair split only have single root then we can just attach the
+ // divider to the single root task in shell.
+ layer = Math.max(layer, SPLIT_DIVIDER_LAYER + 1);
adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer);
t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 51cede0..916fa5b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -102,6 +102,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -169,6 +170,14 @@
private TaskFragment mAdjacentTaskFragment;
/**
+ * Whether to move adjacent task fragment together when re-positioning.
+ *
+ * @see #mAdjacentTaskFragment
+ */
+ // TODO(b/207185041): Remove this once having a single-top root for split screen.
+ boolean mMoveAdjacentTogether;
+
+ /**
* Prevents duplicate calls to onTaskAppeared.
*/
boolean mTaskFragmentAppearedSent;
@@ -232,7 +241,7 @@
/**
* Whether to delay the last activity of TaskFragment being immediately removed while finishing.
* This should only be set on a embedded TaskFragment, where the organizer can have the
- * opportunity to perform other actions or animations.
+ * opportunity to perform animations and finishing the adjacent TaskFragment.
*/
private boolean mDelayLastActivityRemoval;
@@ -313,14 +322,15 @@
return service.mWindowOrganizerController.getTaskFragment(token);
}
- void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
+ void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment, boolean moveTogether) {
if (mAdjacentTaskFragment == taskFragment) {
return;
}
resetAdjacentTaskFragment();
if (taskFragment != null) {
mAdjacentTaskFragment = taskFragment;
- taskFragment.setAdjacentTaskFragment(this);
+ mMoveAdjacentTogether = moveTogether;
+ taskFragment.setAdjacentTaskFragment(this, moveTogether);
}
}
@@ -329,9 +339,11 @@
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
mAdjacentTaskFragment.mDelayLastActivityRemoval = false;
+ mAdjacentTaskFragment.mMoveAdjacentTogether = false;
}
mAdjacentTaskFragment = null;
mDelayLastActivityRemoval = false;
+ mMoveAdjacentTogether = false;
}
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
@@ -1659,8 +1671,8 @@
boolean isAddingActivity = child.asActivityRecord() != null;
final Task task = isAddingActivity ? getTask() : null;
- // If this task had any child before we added this one.
- boolean taskHadChild = task != null && task.hasChild();
+ // If this task had any activity before we added this one.
+ boolean taskHadActivity = task != null && task.getActivity(Objects::nonNull) != null;
// getActivityType() looks at the top child, so we need to read the type before adding
// a new child in case the new child is on top and UNDEFINED.
final int activityType = task != null ? task.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
@@ -1669,7 +1681,7 @@
if (isAddingActivity && task != null) {
child.asActivityRecord().inHistory = true;
- task.onDescendantActivityAdded(taskHadChild, activityType, child.asActivityRecord());
+ task.onDescendantActivityAdded(taskHadActivity, activityType, child.asActivityRecord());
}
}
@@ -1946,7 +1958,15 @@
if (inOutConfig.smallestScreenWidthDp
== Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- if (WindowConfiguration.isFloating(windowingMode)) {
+ // When entering to or exiting from Pip, the PipTaskOrganizer will set the
+ // windowing mode of the activity in the task to WINDOWING_MODE_FULLSCREEN and
+ // temporarily set the bounds of the task to fullscreen size for transitioning.
+ // It will get the wrong value if the calculation is based on this temporary
+ // fullscreen bounds.
+ // We should just inherit the value from parent for this temporary state.
+ final boolean inPipTransition = windowingMode == WINDOWING_MODE_PINNED
+ && !mTmpFullBounds.isEmpty() && mTmpFullBounds.equals(parentBounds);
+ if (WindowConfiguration.isFloating(windowingMode) && !inPipTransition) {
// For floating tasks, calculate the smallest width from the bounds of the task
inOutConfig.smallestScreenWidthDp = (int) (
Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
@@ -2125,13 +2145,7 @@
/** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
private boolean shouldStartChangeTransition(Rect startBounds) {
- if (mWmService.mDisableTransitionAnimation
- || mDisplayContent == null
- || mTaskFragmentOrganizer == null
- || getSurfaceControl() == null
- // The change transition will be covered by display.
- || mDisplayContent.inTransition()
- || !isVisible()) {
+ if (mTaskFragmentOrganizer == null || !canStartChangeTransition()) {
return false;
}
@@ -2177,14 +2191,13 @@
TaskFragmentInfo getTaskFragmentInfo() {
List<IBinder> childActivities = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
- WindowContainer wc = getChildAt(i);
- if (mTaskFragmentOrganizerUid != INVALID_UID
- && wc.asActivityRecord() != null
- && wc.asActivityRecord().info.processName.equals(
- mTaskFragmentOrganizerProcessName)
- && wc.asActivityRecord().getUid() == mTaskFragmentOrganizerUid) {
+ final WindowContainer wc = getChildAt(i);
+ final ActivityRecord ar = wc.asActivityRecord();
+ if (mTaskFragmentOrganizerUid != INVALID_UID && ar != null
+ && ar.info.processName.equals(mTaskFragmentOrganizerProcessName)
+ && ar.getUid() == mTaskFragmentOrganizerUid && !ar.finishing) {
// Only includes Activities that belong to the organizer process for security.
- childActivities.add(wc.asActivityRecord().appToken);
+ childActivities.add(ar.appToken);
}
}
final Point positionInParent = new Point();
@@ -2333,6 +2346,14 @@
return true;
}
+ @Override
+ boolean fillsParent() {
+ // From the perspective of policy, we still want to report that this task fills parent
+ // in fullscreen windowing mode even it doesn't match parent bounds because there will be
+ // letterbox around its real content.
+ return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
+ }
+
boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
boolean printed = false;
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index d911656..c7fdefc 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -24,6 +24,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -31,10 +32,8 @@
import android.util.ArrayMap;
import android.util.Slog;
import android.view.RemoteAnimationDefinition;
-import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
import com.android.internal.protolog.common.ProtoLog;
@@ -135,11 +134,8 @@
void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
- final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(),
- "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared");
try {
- organizer.onTaskFragmentAppeared(
- new TaskFragmentAppearedInfo(info, outSurfaceControl));
+ organizer.onTaskFragmentAppeared(info);
mLastSentTaskFragmentInfos.put(tf, info);
tf.mTaskFragmentAppearedSent = true;
} catch (RemoteException e) {
@@ -434,6 +430,9 @@
private final TaskFragment mTaskFragment;
private final IBinder mErrorCallback;
private final Throwable mException;
+ // Set when the event is deferred due to the host task is invisible. The defer time will
+ // be the last active time of the host task.
+ private long mDeferTime;
private PendingTaskFragmentEvent(TaskFragment taskFragment,
ITaskFragmentOrganizer taskFragmentOrg, @EventType int eventType) {
@@ -503,11 +502,45 @@
|| mPendingTaskFragmentEvents.isEmpty()) {
return;
}
+
+ final ArrayList<Task> visibleTasks = new ArrayList<>();
+ final ArrayList<Task> invisibleTasks = new ArrayList<>();
+ final ArrayList<PendingTaskFragmentEvent> candidateEvents = new ArrayList<>();
for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) {
- PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
- dispatchEvent(event);
+ final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
+ final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
+ if (task != null && (task.lastActiveTime <= event.mDeferTime
+ || !isTaskVisible(task, visibleTasks, invisibleTasks))) {
+ // Defer sending events to the TaskFragment until the host task is active again.
+ event.mDeferTime = task.lastActiveTime;
+ continue;
+ }
+ candidateEvents.add(event);
}
- mPendingTaskFragmentEvents.clear();
+ final int numEvents = candidateEvents.size();
+ for (int i = 0; i < numEvents; i++) {
+ dispatchEvent(candidateEvents.get(i));
+ }
+ if (numEvents > 0) {
+ mPendingTaskFragmentEvents.removeAll(candidateEvents);
+ }
+ }
+
+ private static boolean isTaskVisible(Task task, ArrayList<Task> knownVisibleTasks,
+ ArrayList<Task> knownInvisibleTasks) {
+ if (knownVisibleTasks.contains(task)) {
+ return true;
+ }
+ if (knownInvisibleTasks.contains(task)) {
+ return false;
+ }
+ if (task.shouldBeVisible(null /* starting */)) {
+ knownVisibleTasks.add(task);
+ return true;
+ } else {
+ knownInvisibleTasks.add(task);
+ return false;
+ }
}
void dispatchPendingInfoChangedEvent(TaskFragment taskFragment) {
@@ -547,4 +580,26 @@
event.mException);
}
}
+
+ // TODO(b/204399167): change to push the embedded state to the client side
+ @Override
+ public boolean isActivityEmbedded(IBinder activityToken) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord activity = ActivityRecord.forTokenLocked(activityToken);
+ if (activity == null) {
+ return false;
+ }
+ final TaskFragment taskFragment = activity.getOrganizedTaskFragment();
+ if (taskFragment == null) {
+ return false;
+ }
+ final Task parentTask = taskFragment.getTask();
+ if (parentTask != null) {
+ final Rect taskBounds = parentTask.getBounds();
+ final Rect taskFragBounds = taskFragment.getBounds();
+ return !taskBounds.equals(taskFragBounds) && taskBounds.contains(taskFragBounds);
+ }
+ return false;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 3d5f988..037d582 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.TaskInfo.cameraCompatControlStateToString;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
@@ -931,6 +933,35 @@
}
}
+ @Override
+ public void updateCameraCompatControlState(WindowContainerToken token, int state) {
+ enforceTaskPermission("updateCameraCompatControlState()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
+ if (wc == null) {
+ Slog.w(TAG, "Could not resolve window from token");
+ return;
+ }
+ final Task task = wc.asTask();
+ if (task == null) {
+ Slog.w(TAG, "Could not resolve task from token");
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Update camera compat control state to %s for taskId=%d",
+ cameraCompatControlStateToString(state), task.mTaskId);
+ final ActivityRecord activity = task.getTopNonFinishingActivity();
+ if (activity != null) {
+ activity.updateCameraCompatStateFromUser(state);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
public boolean handleInterceptBackPressedOnTaskRoot(Task task) {
if (task == null || !task.isOrganized()
|| !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 58bc244..5af9147 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2600,6 +2600,13 @@
mSurfaceFreezer.unfreeze(getPendingTransaction());
}
+ /** Whether we can start change transition with this window and current display status. */
+ boolean canStartChangeTransition() {
+ return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
+ && getSurfaceControl() != null && !mDisplayContent.inTransition()
+ && isVisible() && isVisibleRequested() && okToAnimate();
+ }
+
/**
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
*
@@ -2948,12 +2955,7 @@
}
boolean okToAnimate() {
- return okToAnimate(false /* ignoreFrozen */);
- }
-
- boolean okToAnimate(boolean ignoreFrozen) {
- final DisplayContent dc = getDisplayContent();
- return dc != null && dc.okToAnimate(ignoreFrozen);
+ return okToAnimate(false /* ignoreFrozen */, false /* ignoreScreenOn */);
}
boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index bc53041..7956a11 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.isSuspendedState;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.window.WindowProviderService.isWindowProviderService;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
@@ -45,7 +47,7 @@
*
* <ul>
* <li>When a {@link WindowContext} is created, it registers the listener via
- * {@link WindowManagerService#registerWindowContextListener(IBinder, int, int, Bundle)}
+ * {@link WindowManagerService#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)}
* automatically.</li>
* <li>When the {@link WindowContext} adds the first window to the screen via
* {@link android.view.WindowManager#addView(View, android.view.ViewGroup.LayoutParams)},
@@ -53,7 +55,7 @@
* to corresponding {@link WindowToken} via this controller.</li>
* <li>When the {@link WindowContext} is GCed, it unregisters the previously
* registered listener via
- * {@link WindowManagerService#unregisterWindowContextListener(IBinder)}.
+ * {@link WindowManagerService#detachWindowContextFromWindowContainer(IBinder)}.
* {@link WindowManagerService} is also responsible for removing the
* {@link WindowContext} created {@link WindowToken}.</li>
* </ul>
@@ -68,7 +70,7 @@
/**
* Registers the listener to a {@code container} which is associated with
- * a {@code clientToken}, which is a {@link android.app.WindowContext} representation. If the
+ * a {@code clientToken}, which is a {@link android.window.WindowContext} representation. If the
* listener associated with {@code clientToken} hasn't been initialized yet, create one
* {@link WindowContextListenerImpl}. Otherwise, the listener associated with
* {@code clientToken} switches to listen to the {@code container}.
@@ -80,7 +82,7 @@
* @param options a bundle used to pass window-related options.
*/
void registerWindowContainerListener(@NonNull IBinder clientToken,
- @NonNull WindowContainer container, int ownerUid, @WindowType int type,
+ @NonNull WindowContainer<?> container, int ownerUid, @WindowType int type,
@Nullable Bundle options) {
WindowContextListenerImpl listener = mListeners.get(clientToken);
if (listener == null) {
@@ -103,6 +105,16 @@
listener.unregister();
}
+ void dispatchPendingConfigurationIfNeeded(int displayId) {
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ final WindowContextListenerImpl listener = mListeners.valueAt(i);
+ if (listener.getWindowContainer().getDisplayContent().getDisplayId() == displayId
+ && listener.mHasPendingConfiguration) {
+ listener.reportConfigToWindowTokenClient();
+ }
+ }
+ }
+
/**
* Verifies if the caller is allowed to do the operation to the listener specified by
* {@code clientToken}.
@@ -138,7 +150,7 @@
return listener != null ? listener.mOptions : null;
}
- @Nullable WindowContainer getContainer(IBinder clientToken) {
+ @Nullable WindowContainer<?> getContainer(IBinder clientToken) {
final WindowContextListenerImpl listener = mListeners.get(clientToken);
return listener != null ? listener.mContainer : null;
}
@@ -161,9 +173,9 @@
@VisibleForTesting
class WindowContextListenerImpl implements WindowContainerListener {
- @NonNull private final IBinder mClientToken;
+ @NonNull private final IWindowToken mClientToken;
private final int mOwnerUid;
- @NonNull private WindowContainer mContainer;
+ @NonNull private WindowContainer<?> mContainer;
/**
* The options from {@link Context#createWindowContext(int, Bundle)}.
* <p>It can be used for choosing the {@link DisplayArea} where the window context
@@ -177,9 +189,11 @@
private int mLastReportedDisplay = INVALID_DISPLAY;
private Configuration mLastReportedConfig;
- private WindowContextListenerImpl(IBinder clientToken, WindowContainer container,
+ private boolean mHasPendingConfiguration;
+
+ private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container,
int ownerUid, @WindowType int type, @Nullable Bundle options) {
- mClientToken = clientToken;
+ mClientToken = IWindowToken.Stub.asInterface(clientToken);
mContainer = Objects.requireNonNull(container);
mOwnerUid = ownerUid;
mType = type;
@@ -191,17 +205,17 @@
mDeathRecipient = deathRecipient;
} catch (RemoteException e) {
ProtoLog.e(WM_ERROR, "Could not register window container listener token=%s, "
- + "container=%s", mClientToken, mContainer);
+ + "container=%s", clientToken, mContainer);
}
}
/** TEST ONLY: returns the {@link WindowContainer} of the listener */
@VisibleForTesting
- WindowContainer getWindowContainer() {
+ WindowContainer<?> getWindowContainer() {
return mContainer;
}
- private void updateContainer(@NonNull WindowContainer newContainer) {
+ private void updateContainer(@NonNull WindowContainer<?> newContainer) {
Objects.requireNonNull(newContainer);
if (mContainer.equals(newContainer)) {
@@ -214,17 +228,17 @@
}
private void register() {
+ final IBinder token = mClientToken.asBinder();
if (mDeathRecipient == null) {
- throw new IllegalStateException("Invalid client token: " + mClientToken);
+ throw new IllegalStateException("Invalid client token: " + token);
}
- mListeners.putIfAbsent(mClientToken, this);
+ mListeners.putIfAbsent(token, this);
mContainer.registerWindowContainerListener(this);
- reportConfigToWindowTokenClient();
}
private void unregister() {
mContainer.unregisterWindowContainerListener(this);
- mListeners.remove(mClientToken);
+ mListeners.remove(mClientToken.asBinder());
}
private void clear() {
@@ -244,14 +258,27 @@
private void reportConfigToWindowTokenClient() {
if (mDeathRecipient == null) {
- throw new IllegalStateException("Invalid client token: " + mClientToken);
+ throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder());
}
-
+ final DisplayContent dc = mContainer.getDisplayContent();
+ if (!dc.isReady()) {
+ // Do not report configuration when booting. The latest configuration will be sent
+ // when WindowManagerService#displayReady().
+ return;
+ }
+ // If the display of window context associated window container is suspended, don't
+ // report the configuration update. Note that we still dispatch the configuration update
+ // to WindowProviderService to make it compatible with Service#onConfigurationChanged.
+ // Service always receives #onConfigurationChanged callback regardless of display state.
+ if (!isWindowProviderService(mOptions) && isSuspendedState(dc.getDisplayInfo().state)) {
+ mHasPendingConfiguration = true;
+ return;
+ }
+ final Configuration config = mContainer.getConfiguration();
+ final int displayId = dc.getDisplayId();
if (mLastReportedConfig == null) {
mLastReportedConfig = new Configuration();
}
- final Configuration config = mContainer.getConfiguration();
- final int displayId = mContainer.getDisplayContent().getDisplayId();
if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
// No changes since last reported time.
return;
@@ -260,18 +287,18 @@
mLastReportedConfig.setTo(config);
mLastReportedDisplay = displayId;
- IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
try {
- windowTokenClient.onConfigurationChanged(config, displayId);
+ mClientToken.onConfigurationChanged(config, displayId);
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
}
+ mHasPendingConfiguration = false;
}
@Override
public void onRemoved() {
if (mDeathRecipient == null) {
- throw new IllegalStateException("Invalid client token: " + mClientToken);
+ throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder());
}
final WindowToken windowToken = mContainer.asWindowToken();
if (windowToken != null && windowToken.isFromClient()) {
@@ -283,15 +310,14 @@
// If we cannot obtain the DisplayContent, the DisplayContent may also be removed.
// We should proceed the removal process.
if (dc != null) {
- final DisplayArea da = dc.findAreaForToken(windowToken);
+ final DisplayArea<?> da = dc.findAreaForToken(windowToken);
updateContainer(da);
return;
}
}
mDeathRecipient.unlinkToDeath();
- IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
try {
- windowTokenClient.onWindowTokenRemoved();
+ mClientToken.onWindowTokenRemoved();
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
}
@@ -300,7 +326,7 @@
@Override
public String toString() {
- return "WindowContextListenerImpl{clientToken=" + mClientToken + ", "
+ return "WindowContextListenerImpl{clientToken=" + mClientToken.asBinder() + ", "
+ "container=" + mContainer + "}";
}
@@ -314,11 +340,11 @@
}
void linkToDeath() throws RemoteException {
- mClientToken.linkToDeath(this, 0);
+ mClientToken.asBinder().linkToDeath(this, 0);
}
void unlinkToDeath() {
- mClientToken.unlinkToDeath(this, 0);
+ mClientToken.asBinder().unlinkToDeath(this, 0);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 943a3c1..4258e07 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -154,7 +154,6 @@
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -164,7 +163,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.TestUtilityService;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -1063,10 +1061,6 @@
final HighRefreshRateDenylist mHighRefreshRateDenylist;
- // Maintainer of a collection of all possible DisplayInfo for all configurations of the
- // logical displays.
- final PossibleDisplayInfoMapper mPossibleDisplayInfoMapper;
-
// If true, only the core apps and services are being launched because the device
// is in a special boot mode, such as being encrypted or waiting for a decryption password.
// For example, when this flag is true, there will be no wallpaper service.
@@ -1242,7 +1236,6 @@
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
- mPossibleDisplayInfoMapper = new PossibleDisplayInfoMapper(mDisplayManagerInternal);
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
@@ -1684,6 +1677,7 @@
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
win.setRequestedVisibilities(requestedVisibilities);
+ attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != ADD_OKAY) {
@@ -2250,6 +2244,7 @@
if (attrs != null) {
displayPolicy.adjustWindowParamsLw(win, attrs);
win.mToken.adjustWindowParams(win, attrs);
+ attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);
int disableFlags =
(attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
@@ -2741,6 +2736,9 @@
@Override
public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
type, int displayId, Bundle options) {
+ if (clientToken == null) {
+ throw new IllegalArgumentException("clientToken must not be null!");
+ }
final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
"attachWindowContextToDisplayArea", false /* printLog */);
final int callingUid = Binder.getCallingUid();
@@ -2831,6 +2829,39 @@
}
}
+ @Override
+ public Configuration attachToDisplayContent(IBinder clientToken, int displayId) {
+ if (clientToken == null) {
+ throw new IllegalArgumentException("clientToken must not be null!");
+ }
+ final int callingUid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ // We use "getDisplayContent" instead of "getDisplayContentOrCreate" because
+ // this method may be called in DisplayPolicy's constructor and may cause
+ // infinite loop. In this scenario, we early return here and switch to do the
+ // registration in DisplayContent#onParentChanged at DisplayContent initialization.
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ if (Binder.getCallingPid() != myPid()) {
+ throw new WindowManager.InvalidDisplayException("attachToDisplayContent: "
+ + "trying to attach to a non-existing display:" + displayId);
+ }
+ // Early return if this method is invoked from system process.
+ // See above comments for more detail.
+ return null;
+ }
+
+ mWindowContextListenerController.registerWindowContainerListener(clientToken, dc,
+ callingUid, INVALID_WINDOW_TYPE, null /* options */);
+ return dc.getConfiguration();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
/** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
@@ -6142,9 +6173,10 @@
+ " callers=" + Debug.getCallers(3));
return NAV_BAR_INVALID;
}
- displayContent.performLayout(false /* initial */,
- false /* updateInputWindows */);
- return displayContent.getDisplayPolicy().getNavBarPosition();
+ return displayContent.getDisplayPolicy().navigationBarPosition(
+ displayContent.mBaseDisplayWidth,
+ displayContent.mBaseDisplayHeight,
+ displayContent.getDisplayRotation().getRotation());
}
}
@@ -8300,6 +8332,23 @@
}
/**
+ * You need ALLOW_SLIPPERY_TOUCHES permission to be able to set FLAG_SLIPPERY.
+ */
+ private int sanitizeFlagSlippery(int flags, String windowName, int callingUid, int callingPid) {
+ if ((flags & FLAG_SLIPPERY) == 0) {
+ return flags;
+ }
+ final int permissionResult = mContext.checkPermission(
+ android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES, callingPid, callingUid);
+ if (permissionResult != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Removing FLAG_SLIPPERY from '" + windowName
+ + "' because it doesn't have ALLOW_SLIPPERY_TOUCHES permission");
+ return flags & ~FLAG_SLIPPERY;
+ }
+ return flags;
+ }
+
+ /**
* Assigns an InputChannel to a SurfaceControl and configures it to receive
* touch input according to it's on-screen geometry.
*
@@ -8338,7 +8387,7 @@
h.setWindowToken(window);
h.name = name;
- flags = DisplayPolicy.sanitizeFlagSlippery(flags, privateFlags, name);
+ flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid);
final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE
| FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE);
@@ -8536,52 +8585,6 @@
}
}
- @Override
- public List<DisplayInfo> getPossibleDisplayInfo(int displayId, String packageName) {
- final int callingUid = Binder.getCallingUid();
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- if (packageName == null || !isRecentsComponent(packageName, callingUid)) {
- Slog.e(TAG, "Unable to verify uid for package " + packageName
- + " for getPossibleMaximumWindowMetrics");
- return new ArrayList<>();
- }
-
- // Retrieve the DisplayInfo for all possible rotations across all possible display
- // layouts.
- return List.copyOf(mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId));
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- /**
- * Returns {@code true} when the calling package is the recents component.
- */
- boolean isRecentsComponent(@NonNull String callingPackageName, int callingUid) {
- String recentsPackage;
- try {
- String recentsComponent = mContext.getResources().getString(
- R.string.config_recentsComponentName);
- if (recentsComponent == null) {
- return false;
- }
- recentsPackage = ComponentName.unflattenFromString(recentsComponent).getPackageName();
- } catch (Resources.NotFoundException e) {
- Slog.e(TAG, "Unable to verify if recents component", e);
- return false;
- }
- try {
- return callingUid == mContext.getPackageManager().getPackageUid(callingPackageName, 0)
- && callingPackageName.equals(recentsPackage);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Unable to verify if recents component", e);
- return false;
- }
- }
-
void grantEmbeddedWindowFocus(Session session, IBinder inputToken, boolean grantFocus) {
synchronized (mGlobalLock) {
final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 0f8587c..a94fd07 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,16 +19,6 @@
import static android.os.Build.IS_USER;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_CENTER;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_LEFT;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_RIGHT;
-
-import android.content.res.Resources.NotFoundException;
-import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -46,8 +36,6 @@
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.LetterboxReachabilityPosition;
import java.io.IOException;
import java.io.PrintWriter;
@@ -70,12 +58,10 @@
// Internal service impl -- must perform security checks before touching.
private final WindowManagerService mInternal;
- private final LetterboxConfiguration mLetterboxConfiguration;
public WindowManagerShellCommand(WindowManagerService service) {
mInterface = service;
mInternal = service;
- mLetterboxConfiguration = service.mLetterboxConfiguration;
}
@Override
@@ -127,14 +113,6 @@
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
- case "set-letterbox-style":
- return runSetLetterboxStyle(pw);
- case "get-letterbox-style":
- return runGetLetterboxStyle(pw);
- case "reset-letterbox-style":
- return runResetLetterboxStyle(pw);
- case "set-sandbox-display-apis":
- return runSandboxDisplayApis(pw);
case "set-multi-window-config":
return runSetMultiWindowConfig();
case "get-multi-window-config":
@@ -353,37 +331,6 @@
return 0;
}
- /**
- * Override display size and metrics to reflect the DisplayArea of the calling activity.
- */
- private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException {
- int displayId = Display.DEFAULT_DISPLAY;
- String arg = getNextArgRequired();
- if ("-d".equals(arg)) {
- displayId = Integer.parseInt(getNextArgRequired());
- arg = getNextArgRequired();
- }
-
- final boolean sandboxDisplayApis;
- switch (arg) {
- case "true":
- case "1":
- sandboxDisplayApis = true;
- break;
- case "false":
- case "0":
- sandboxDisplayApis = false;
- break;
- default:
- getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
- + "get " + arg);
- return -1;
- }
-
- mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis);
- return 0;
- }
-
private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
mInterface.dismissKeyguard(null /* callback */, null /* message */);
return 0;
@@ -601,318 +548,6 @@
return 0;
}
- private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
- final float aspectRatio;
- try {
- String arg = getNextArgRequired();
- aspectRatio = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad aspect ratio format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: aspect ratio should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
- }
- return 0;
- }
-
- private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
- final int cornersRadius;
- try {
- String arg = getNextArgRequired();
- cornersRadius = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad corners radius format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: corners radius should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
- @LetterboxBackgroundType final int backgroundType;
- try {
- String arg = getNextArgRequired();
- switch (arg) {
- case "solid_color":
- backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
- break;
- case "app_color_background":
- backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
- break;
- case "app_color_background_floating":
- backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
- break;
- case "wallpaper":
- backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
- break;
- default:
- getErrPrintWriter().println(
- "Error: 'solid_color', 'app_color_background' or "
- + "'wallpaper' should be provided as an argument");
- return -1;
- }
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'solid_color', 'app_color_background' or "
- + "'wallpaper' should be provided as an argument" + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundColorResource(PrintWriter pw) throws RemoteException {
- final int colorId;
- try {
- String arg = getNextArgRequired();
- colorId = mInternal.mContext.getResources()
- .getIdentifier(arg, "color", "com.android.internal");
- } catch (NotFoundException e) {
- getErrPrintWriter().println(
- "Error: color in '@android:color/resource_name' format should be provided as "
- + "an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundColorResourceId(colorId);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
- final Color color;
- try {
- String arg = getNextArgRequired();
- color = Color.valueOf(Color.parseColor(arg));
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: color in #RRGGBB format should be provided as "
- + "an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundColor(color);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
- throws RemoteException {
- final int radius;
- try {
- String arg = getNextArgRequired();
- radius = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: blur radius format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: blur radius should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
- throws RemoteException {
- final float alpha;
- try {
- String arg = getNextArgRequired();
- alpha = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad alpha format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: alpha should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
- }
- return 0;
- }
-
- private int runSetLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
- final float multiplier;
- try {
- String arg = getNextArgRequired();
- multiplier = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad multiplier format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: multiplier should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
- }
- return 0;
- }
-
- private int runSetLetterboxIsReachabilityEnabled(PrintWriter pw) throws RemoteException {
- String arg = getNextArg();
- final boolean enabled;
- switch (arg) {
- case "true":
- case "1":
- enabled = true;
- break;
- case "false":
- case "0":
- enabled = false;
- break;
- default:
- getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
- return -1;
- }
-
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setIsReachabilityEnabled(enabled);
- }
- return 0;
- }
-
- private int runSetLetterboxDefaultPositionForReachability(PrintWriter pw)
- throws RemoteException {
- @LetterboxReachabilityPosition final int position;
- try {
- String arg = getNextArgRequired();
- switch (arg) {
- case "left":
- position = LETTERBOX_REACHABILITY_POSITION_LEFT;
- break;
- case "center":
- position = LETTERBOX_REACHABILITY_POSITION_CENTER;
- break;
- case "right":
- position = LETTERBOX_REACHABILITY_POSITION_RIGHT;
- break;
- default:
- getErrPrintWriter().println(
- "Error: 'left', 'center' or 'right' are expected as an argument");
- return -1;
- }
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'left', 'center' or 'right' are expected as an argument" + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setDefaultPositionForReachability(position);
- }
- return 0;
- }
-
- private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
- if (peekNextArg() == null) {
- getErrPrintWriter().println("Error: No arguments provided.");
- }
- while (peekNextArg() != null) {
- String arg = getNextArg();
- switch (arg) {
- case "--aspectRatio":
- runSetFixedOrientationLetterboxAspectRatio(pw);
- break;
- case "--cornerRadius":
- runSetLetterboxActivityCornersRadius(pw);
- break;
- case "--backgroundType":
- runSetLetterboxBackgroundType(pw);
- break;
- case "--backgroundColor":
- runSetLetterboxBackgroundColor(pw);
- break;
- case "--backgroundColorResource":
- runSetLetterboxBackgroundColorResource(pw);
- break;
- case "--wallpaperBlurRadius":
- runSetLetterboxBackgroundWallpaperBlurRadius(pw);
- break;
- case "--wallpaperDarkScrimAlpha":
- runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
- break;
- case "--horizontalPositionMultiplier":
- runSetLetterboxHorizontalPositionMultiplier(pw);
- break;
- case "--isReachabilityEnabled":
- runSetLetterboxIsReachabilityEnabled(pw);
- break;
- case "--defaultPositionForReachability":
- runSetLetterboxDefaultPositionForReachability(pw);
- break;
- default:
- getErrPrintWriter().println(
- "Error: Unrecognized letterbox style option: " + arg);
- return -1;
- }
- }
- return 0;
- }
-
- private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
- if (peekNextArg() == null) {
- resetLetterboxStyle();
- }
- synchronized (mInternal.mGlobalLock) {
- while (peekNextArg() != null) {
- String arg = getNextArg();
- switch (arg) {
- case "aspectRatio":
- mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
- break;
- case "cornerRadius":
- mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
- break;
- case "backgroundType":
- mLetterboxConfiguration.resetLetterboxBackgroundType();
- break;
- case "backgroundColor":
- mLetterboxConfiguration.resetLetterboxBackgroundColor();
- break;
- case "wallpaperBlurRadius":
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
- break;
- case "wallpaperDarkScrimAlpha":
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
- break;
- case "horizontalPositionMultiplier":
- mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
- break;
- case "isReachabilityEnabled":
- mLetterboxConfiguration.getIsReachabilityEnabled();
- break;
- case "defaultPositionForReachability":
- mLetterboxConfiguration.getDefaultPositionForReachability();
- break;
- default:
- getErrPrintWriter().println(
- "Error: Unrecognized letterbox style option: " + arg);
- return -1;
- }
- }
- }
- return 0;
- }
-
private int runSetMultiWindowConfig() {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -987,47 +622,6 @@
return 0;
}
- private void resetLetterboxStyle() {
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
- mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
- mLetterboxConfiguration.resetLetterboxBackgroundType();
- mLetterboxConfiguration.resetLetterboxBackgroundColor();
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
- mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
- mLetterboxConfiguration.resetIsReachabilityEnabled();
- mLetterboxConfiguration.resetDefaultPositionForReachability();
- }
- }
-
- private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
- synchronized (mInternal.mGlobalLock) {
- pw.println("Corner radius: "
- + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
- pw.println("Horizontal position multiplier: "
- + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
- pw.println("Aspect ratio: "
- + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
- pw.println("Is reachability enabled: "
- + mLetterboxConfiguration.getIsReachabilityEnabled());
- pw.println("Default position for reachability: "
- + LetterboxConfiguration.letterboxReachabilityPositionToString(
- mLetterboxConfiguration.getDefaultPositionForReachability()));
-
- pw.println("Background type: "
- + LetterboxConfiguration.letterboxBackgroundTypeToString(
- mLetterboxConfiguration.getLetterboxBackgroundType()));
- pw.println(" Background color: " + Integer.toHexString(
- mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
- pw.println(" Wallpaper blur radius: "
- + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
- pw.println(" Wallpaper dark scrim alpha: "
- + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
- }
- return 0;
- }
-
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -1052,12 +646,6 @@
// set-ignore-orientation-request
mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
- // set-letterbox-style
- resetLetterboxStyle();
-
- // set-sandbox-display-apis
- mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
-
// set-multi-window-config
runResetMultiWindowConfig();
@@ -1092,12 +680,7 @@
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
- pw.println(" set-sandbox-display-apis [true|1|false|0]");
- pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
- pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or");
- pw.println(" Size Compat Mode.");
- printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
pw.println(" reset [-d DISPLAY_ID]");
@@ -1110,61 +693,6 @@
}
}
- private void printLetterboxHelp(PrintWriter pw) {
- pw.println(" set-letterbox-style");
- pw.println(" Sets letterbox style using the following options:");
- pw.println(" --aspectRatio aspectRatio");
- pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
- + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
- pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
- pw.println(" be ignored and framework implementation will determine aspect ratio.");
- pw.println(" --cornerRadius radius");
- pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
- pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
- pw.println(" ignored and corners of the activity won't be rounded.");
- pw.println(" --backgroundType [reset|solid_color|app_color_background");
- pw.println(" |app_color_background_floating|wallpaper]");
- pw.println(" Type of background used in the letterbox mode.");
- pw.println(" --backgroundColor color");
- pw.println(" Color of letterbox which is be used when letterbox background type");
- pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control");
- pw.println(" letterbox background type. See Color#parseColor for allowed color");
- pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
- pw.println(" --backgroundColorResource resource_name");
- pw.println(" Color resource name of letterbox background which is used when");
- pw.println(" background type is 'solid-color'. Use (set)get-letterbox-style to");
- pw.println(" check and control background type. Parameter is a color resource");
- pw.println(" name, for example, @android:color/system_accent2_50.");
- pw.println(" --wallpaperBlurRadius radius");
- pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
- pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
- pw.println(" are ignored and 0 is used.");
- pw.println(" --wallpaperDarkScrimAlpha alpha");
- pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'");
- pw.println(" letterbox background. If alpha < 0 or >= 1 both it and");
- pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
- pw.println(" and 0.0 (transparent) is used instead.");
- pw.println(" --horizontalPositionMultiplier multiplier");
- pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,");
- pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
- pw.println(" are ignored and central position (0.5) is used.");
- pw.println(" --isReachabilityEnabled [true|1|false|0]");
- pw.println(" Whether reachability repositioning is allowed for letterboxed");
- pw.println(" fullscreen apps in landscape device orientation.");
- pw.println(" --defaultPositionForReachability [left|center|right]");
- pw.println(" Default horizontal position of app window when reachability is.");
- pw.println(" enabled.");
- pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
- pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
- pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled");
- pw.println(" |defaultPositionMultiplierForReachability]");
- pw.println(" Resets overrides to default values for specified properties separated");
- pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
- pw.println(" If no arguments provided, all values will be reset.");
- pw.println(" get-letterbox-style");
- pw.println(" Prints letterbox style configuration.");
- }
-
private void printMultiWindowConfigHelp(PrintWriter pw) {
pw.println(" set-multi-window-config");
pw.println(" Sets options to determine if activity should be shown in multi window:");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index bd8d116..42766bd 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -588,7 +588,7 @@
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
final TaskFragmentCreationParams taskFragmentCreationOptions =
hop.getTaskFragmentCreationOptions();
- createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
+ createTaskFragment(taskFragmentCreationOptions, errorCallbackToken, caller);
break;
}
case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
@@ -631,7 +631,7 @@
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
final int result = mService.getActivityStartController()
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
- hop.getCallingActivity());
+ hop.getCallingActivity(), caller.mUid, caller.mPid);
if (!isStartResultSuccessful(result)) {
sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
errorCallbackToken,
@@ -665,7 +665,7 @@
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
- tf1.setAdjacentTaskFragment(tf2);
+ tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
final Bundle bundle = hop.getLaunchOptions();
@@ -978,7 +978,7 @@
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
}
- root1.setAdjacentTaskFragment(root2);
+ root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
return TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -1200,7 +1200,7 @@
}
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
- @Nullable IBinder errorCallbackToken) {
+ @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
final ActivityRecord ownerActivity =
ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface(
@@ -1218,9 +1218,9 @@
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
- // The ownerActivity has to belong to the same app as the root Activity of the target Task.
- final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
- if (rootActivity.getUid() != ownerActivity.getUid()) {
+ // The ownerActivity has to belong to the same app as the target Task.
+ if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
+ || ownerActivity.getTask().effectiveUid != caller.mUid) {
final Throwable exception =
new IllegalArgumentException("Not allowed to operate with the ownerToken while "
+ "the root activity of the target task belong to the different app");
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1cfbe07..3ccb06c 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -512,19 +512,19 @@
*/
@HotPath(caller = HotPath.START_SERVICE)
public boolean areBackgroundFgsStartsAllowed() {
- return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed(),
+ return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState(),
true /* isCheckingForFgsStart */);
}
- boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) {
- return areBackgroundActivityStartsAllowed(appSwitchAllowed,
+ boolean areBackgroundActivityStartsAllowed(int appSwitchState) {
+ return areBackgroundActivityStartsAllowed(appSwitchState,
false /* isCheckingForFgsStart */);
}
- private boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed,
+ private boolean areBackgroundActivityStartsAllowed(int appSwitchState,
boolean isCheckingForFgsStart) {
return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
- appSwitchAllowed, isCheckingForFgsStart, hasActivityInVisibleTask(),
+ appSwitchState, isCheckingForFgsStart, hasActivityInVisibleTask(),
mInstrumentingWithBackgroundActivityStartPrivileges,
mAtm.getLastStopAppSwitchesTime(),
mLastActivityLaunchTime, mLastActivityFinishTime);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e0c3cf9..a16d9c1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -51,7 +51,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -2882,10 +2881,9 @@
}
}
- int getSurfaceTouchableRegion(Region region, int flags) {
- final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+ void getSurfaceTouchableRegion(Region region, WindowManager.LayoutParams attrs) {
+ final boolean modal = attrs.isModal();
if (modal) {
- flags |= FLAG_NOT_TOUCH_MODAL;
if (mActivityRecord != null) {
// Limit the outer touch to the activity root task region.
updateRegionForModalActivityWindow(region);
@@ -2917,8 +2915,6 @@
if (mInvGlobalScale != 1.f) {
region.scale(mInvGlobalScale);
}
-
- return flags;
}
/**
@@ -3618,7 +3614,10 @@
}
// Exclude toast because legacy apps may show toast window by themselves, so the misused
// apps won't always be considered as foreground state.
- if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST) {
+ // Exclude private presentations as they can only be shown on private virtual displays and
+ // shouldn't be the cause of an app be considered foreground.
+ if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST
+ && mAttrs.type != TYPE_PRIVATE_PRESENTATION) {
mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
}
if (mIsImWindow && mWmService.mAccessibilityController.hasCallbacks()) {
@@ -3766,10 +3765,9 @@
* {@link WindowManager.LayoutParams#FLAG_NOT_TOUCH_MODAL touch modality.}
*/
void getEffectiveTouchableRegion(Region outRegion) {
- final boolean modal = (mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
final DisplayContent dc = getDisplayContent();
- if (modal && dc != null) {
+ if (mAttrs.isModal() && dc != null) {
outRegion.set(dc.getBounds());
cropRegionToRootTaskBoundsIfNeeded(outRegion);
subtractTouchExcludeRegionIfNeeded(outRegion);
@@ -5069,6 +5067,48 @@
return false;
}
+ private boolean shouldFinishAnimatingExit() {
+ // Exit animation might be applied soon.
+ if (inTransition()) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isTransition: %s",
+ this);
+ return false;
+ }
+ if (!mDisplayContent.okToAnimate()) {
+ return true;
+ }
+ // Exit animation is running.
+ if (isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES)) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isAnimating: %s",
+ this);
+ return false;
+ }
+ // If the wallpaper is currently behind this app window, we need to change both of
+ // them inside of a transaction to avoid artifacts.
+ if (mDisplayContent.mWallpaperController.isWallpaperTarget(this)) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
+ "shouldWaitAnimatingExit: isWallpaperTarget: %s", this);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * If this is window is stuck in the animatingExit status, resume clean up procedure blocked
+ * by the exit animation.
+ */
+ void cleanupAnimatingExitWindow() {
+ // TODO(b/205335975): WindowManagerService#tryStartExitingAnimation starts an exit animation
+ // and set #mAnimationExit. After the exit animation finishes, #onExitAnimationDone shall
+ // be called, but there seems to be a case that #onExitAnimationDone is not triggered, so
+ // a windows stuck in the animatingExit status.
+ if (mAnimatingExit && shouldFinishAnimatingExit()) {
+ ProtoLog.w(WM_DEBUG_APP_TRANSITIONS, "Clear window stuck on animatingExit status: %s",
+ this);
+ onExitAnimationDone();
+ }
+ }
+
void onExitAnimationDone() {
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 22c4feb..d0ca298 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1995,13 +1995,12 @@
final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid));
ActiveAdmin admin = policy.mAdminMap.get(adminComponent);
- if (admin == null) {
+ // Throwing combined exception message for both the cases here, because from different
+ // security exceptions it could be deduced if particular package is admin package.
+ if (admin == null || admin.getUid() != callerUid) {
throw new SecurityException(String.format(
- "No active admin for %s", adminComponent));
- }
- if (admin.getUid() != callerUid) {
- throw new SecurityException(String.format(
- "Admin %s is not owned by uid %d", adminComponent, callerUid));
+ "Admin %s does not exist or is not owned by uid %d", adminComponent,
+ callerUid));
}
if (callerPackage != null) {
Preconditions.checkArgument(callerPackage.equals(adminComponent.getPackageName()));
@@ -3797,7 +3796,8 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)
+ ? getCallerIdentity() : getCallerIdentity(adminReceiver);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN);
enforceUserUnlocked(userHandle);
@@ -3814,8 +3814,7 @@
+ adminReceiver);
return;
}
- Preconditions.checkCallAuthorization(admin.getUid() == caller.getUid()
- || hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
mInjector.binderWithCleanCallingIdentity(() ->
removeActiveAdminLocked(adminReceiver, userHandle));
}
@@ -8181,17 +8180,16 @@
*/
@Override
public boolean getCameraDisabled(ComponentName who, int userHandle, boolean parent) {
- return getCameraDisabled(who, userHandle, /* mergeDeviceOwnerRestriction= */ true, parent);
- }
-
- private boolean getCameraDisabled(ComponentName who, int userHandle,
- boolean mergeDeviceOwnerRestriction, boolean parent) {
if (!mHasFeature) {
return false;
}
+
+ final CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+
if (parent) {
Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
+ isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()));
}
synchronized (getLockObject()) {
@@ -8200,17 +8198,15 @@
return (admin != null) && admin.disableCamera;
}
// First, see if DO has set it. If so, it's device-wide.
- if (mergeDeviceOwnerRestriction) {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner != null && deviceOwner.disableCamera) {
- return true;
- }
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner != null && deviceOwner.disableCamera) {
+ return true;
}
final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
// Return the strictest policy across all participating admins.
List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(affectedUserId);
// Determine whether or not the device camera is disabled for any active admins.
- for (ActiveAdmin admin: admins) {
+ for (ActiveAdmin admin : admins) {
if (admin.disableCamera) {
return true;
}
@@ -8274,6 +8270,9 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(
+ who == null || isCallingFromPackage(who.getPackageName(), caller.getUid())
+ || isSystemUid(caller));
final long ident = mInjector.binderClearCallingIdentity();
try {
diff --git a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
index 49d5e50..d3353cd 100644
--- a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
+++ b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
@@ -31,6 +31,7 @@
import com.android.server.LocalServices;
import com.android.server.people.PeopleServiceInternal;
+import com.android.server.pm.PackageManagerService;
/**
* If a {@link ConversationStatus} is added to the system with an expiration time, remove that
@@ -50,6 +51,7 @@
final PendingIntent pi = PendingIntent.getBroadcast(context,
REQUEST_CODE,
new Intent(ACTION)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
.setData(new Uri.Builder().scheme(SCHEME)
.appendPath(getKey(userId, pkg, conversationId, status))
.build())
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 18f1267..0dd4f5b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
@@ -1854,6 +1855,36 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoAll_BoundByPersService_Cycle_Branch_Capability() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ bindService(app, client, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+ MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+ bindService(client, client2, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ bindService(client2, app, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
+ MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
+ client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+ bindService(app, client3, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
+ lru.clear();
+ lru.add(app);
+ lru.add(client);
+ lru.add(client2);
+ lru.add(client3);
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertEquals(PROCESS_CAPABILITY_ALL, client.mState.getSetCapability());
+ assertEquals(PROCESS_CAPABILITY_ALL, client2.mState.getSetCapability());
+ assertEquals(PROCESS_CAPABILITY_ALL, app.mState.getSetCapability());
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoAll_Provider_Cycle_Branch_2() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
index e2e7f5d..94dcdf9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
@@ -58,72 +58,86 @@
@Test
public void testLocationMonitoring() {
CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
- Object key1 = new Object();
- Object key2 = new Object();
CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
- Object key3 = new Object();
- Object key4 = new Object();
- mHelper.reportLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
+ mHelper.reportLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
+ mHelper.reportLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
+ mHelper.reportLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
- mHelper.reportLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
+ mHelper.reportLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
- mHelper.reportLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
- mHelper.reportLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
+ mHelper.reportLocationStop(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ mHelper.reportLocationStop(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller1));
- mHelper.reportLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
- mHelper.reportLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
+ mHelper.reportLocationStop(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ mHelper.reportLocationStop(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller2));
}
@Test
public void testHighPowerLocationMonitoring() {
CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
- Object key1 = new Object();
- Object key2 = new Object();
CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
- Object key3 = new Object();
- Object key4 = new Object();
- mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ mHelper.reportHighPowerLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ mHelper.reportHighPowerLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ mHelper.reportHighPowerLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
- mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ mHelper.reportHighPowerLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
- mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ mHelper.reportHighPowerLocationStop(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ mHelper.reportHighPowerLocationStop(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ mHelper.reportHighPowerLocationStop(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ mHelper.reportHighPowerLocationStop(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index d0b2eda..890a549 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -845,6 +845,48 @@
}
@Test
+ public void testLocationMonitoring_multipleIdentities() {
+ CallerIdentity identity1 = CallerIdentity.forTest(CURRENT_USER, 1,
+ "mypackage", "attribution", "listener1");
+ CallerIdentity identity2 = CallerIdentity.forTest(CURRENT_USER, 1,
+ "mypackage", "attribution", "listener2");
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ IDENTITY.getPackageName())).isFalse();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ IDENTITY.getPackageName())).isFalse();
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build();
+ mManager.registerLocationRequest(request1, identity1, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build();
+ mManager.registerLocationRequest(request2, identity2, PERMISSION_FINE, listener2);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isTrue();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener2);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isTrue();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener1);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isFalse();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isFalse();
+ }
+
+ @Test
public void testProviderRequest() {
assertThat(mProvider.getRequest().isActive()).isFalse();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 4d6f49e..4eba219 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -90,6 +90,19 @@
}
@Test
+ public void testThrottle_lowInterval() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(0).build();
+
+ mProvider.getController().setRequest(request);
+ mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
+ verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mListener, after(1500).times(2)).onReportLocation(any(LocationResult.class));
+ }
+
+ @Test
public void testThrottle_stationaryExit() {
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
@@ -104,17 +117,16 @@
mInjector.getDeviceIdleHelper().setIdle(true);
verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
- verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceStationaryHelper().setStationary(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
public void testThrottle_idleExit() {
- ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1000).build();
mProvider.getController().setRequest(request);
verify(mDelegate).onSetRequest(request);
@@ -127,17 +139,16 @@
mInjector.getDeviceStationaryHelper().setStationary(true);
verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
- verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceIdleHelper().setIdle(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
public void testThrottle_NoInitialLocation() {
- ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1000).build();
mProvider.getController().setRequest(request);
verify(mDelegate).onSetRequest(request);
@@ -149,11 +160,11 @@
mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
verify(mDelegate, times(1)).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceStationaryHelper().setStationary(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(2)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index fd97557..e053dc3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -29,6 +29,7 @@
import com.android.server.apphibernation.AppHibernationService
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.testutils.whenever
+import org.junit.Assert
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -100,8 +101,11 @@
rule.system().dataAppDirectory)
val pm = createPackageManagerService()
rule.system().validateFinalState()
- val ps = pm.getPackageSetting(TEST_PACKAGE_NAME)
- ps!!.setStopped(true, TEST_USER_ID)
+
+ TestableLooper.get(this).processAllMessages()
+
+ whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(true)
pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
@@ -112,6 +116,31 @@
}
@Test
+ fun testExitForceStop_nonExistingAppHibernationManager_doesNotThrowException() {
+ whenever(rule.mocks().injector.getLocalService(AppHibernationManagerInternal::class.java))
+ .thenReturn(null)
+
+ rule.system().stageScanExistingPackage(
+ TEST_PACKAGE_NAME,
+ 1L,
+ rule.system().dataAppDirectory)
+ val pm = createPackageManagerService()
+ rule.system().validateFinalState()
+
+ TestableLooper.get(this).processAllMessages()
+
+ whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(true)
+
+ try {
+ pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
+ TestableLooper.get(this).processAllMessages()
+ } catch (e: Exception) {
+ Assert.fail("Method throws exception when AppHibernationManager is not ready.\n$e")
+ }
+ }
+
+ @Test
fun testGetOptimizablePackages_ExcludesGloballyHibernatingPackages() {
rule.system().stageScanExistingPackage(
TEST_PACKAGE_NAME,
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/LocalColorRepositoryTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/LocalColorRepositoryTest.java
new file mode 100644
index 0000000..ea7a9a4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/LocalColorRepositoryTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wallpaper;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import static java.util.Arrays.asList;
+
+import android.app.ILocalWallpaperColorConsumer;
+import android.app.WallpaperColors;
+import android.graphics.RectF;
+import android.os.IBinder;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import android.util.ArraySet;
+import java.util.List;
+import java.util.function.Consumer;
+
+
+@RunWith(AndroidJUnit4.class)
+public class LocalColorRepositoryTest {
+ private LocalColorRepository mRepo = new LocalColorRepository();
+ @Mock
+ private IBinder mBinder1;
+ @Mock
+ private IBinder mBinder2;
+ @Mock
+ private ILocalWallpaperColorConsumer mCallback1;
+ @Mock
+ private ILocalWallpaperColorConsumer mCallback2;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ when(mCallback1.asBinder()).thenReturn(mBinder1);
+ when(mCallback2.asBinder()).thenReturn(mBinder2);
+ }
+
+ @Test
+ public void testDisplayAreas() {
+ RectF area1 = new RectF(1, 0, 0, 0);
+ RectF area2 = new RectF(2, 1, 1, 1);
+ ArraySet<RectF> expectedAreas = new ArraySet(asList(area1, area2));
+
+ mRepo.addAreas(mCallback1, asList(area1), 0);
+ mRepo.addAreas(mCallback2, asList(area2), 0);
+ mRepo.addAreas(mCallback1, asList(new RectF(3, 1, 1, 1)), 1);
+
+ assertEquals(expectedAreas, new ArraySet(mRepo.getAreasByDisplayId(0)));
+ assertEquals(new ArraySet(asList(new RectF(3, 1, 1, 1))),
+ new ArraySet(mRepo.getAreasByDisplayId(1)));
+ assertEquals(new ArraySet(), new ArraySet(mRepo.getAreasByDisplayId(2)));
+ }
+
+ @Test
+ public void testAddAndRemoveAreas() {
+ RectF area1 = new RectF(1, 0, 0, 0);
+ RectF area2 = new RectF(2, 1, 1, 1);
+
+ mRepo.addAreas(mCallback1, asList(area1), 0);
+ mRepo.addAreas(mCallback1, asList(area2), 0);
+ mRepo.addAreas(mCallback2, asList(area2), 1);
+
+ List<RectF> removed = mRepo.removeAreas(mCallback1, asList(area1), 0);
+ assertEquals(new ArraySet(asList(area1)), new ArraySet(removed));
+ // since we have another callback with a different area, we don't purge rid of any areas
+ removed = mRepo.removeAreas(mCallback1, asList(area2), 0);
+ assertEquals(new ArraySet(), new ArraySet(removed));
+ }
+
+ @Test
+ public void testAreaCallback() {
+ Consumer<ILocalWallpaperColorConsumer> consumer = mock(Consumer.class);
+ WallpaperColors colors = mock(WallpaperColors.class);
+ RectF area1 = new RectF(1, 0, 0, 0);
+ RectF area2 = new RectF(2, 1, 1, 1);
+
+ mRepo.addAreas(mCallback1, asList(area1), 0);
+ mRepo.addAreas(mCallback1, asList(area2), 0);
+ mRepo.addAreas(mCallback2, asList(area2), 0);
+
+ mRepo.forEachCallback(consumer, area1, 0);
+ Mockito.verify(consumer, times(1)).accept(eq(mCallback1));
+ Mockito.verify(consumer, times(0)).accept(eq(mCallback2));
+ mRepo.forEachCallback(consumer, area2, 0);
+ Mockito.verify(consumer, times(2)).accept(eq(mCallback1));
+ Mockito.verify(consumer, times(1)).accept(eq(mCallback2));
+ }
+
+ @Test
+ public void unregisterCallbackWhenNoAreas() {
+ RectF area1 = new RectF(1, 0, 0, 0);
+ RectF area2 = new RectF(2, 1, 1, 1);
+
+ assertFalse(mRepo.isCallbackAvailable(mCallback1));
+
+ mRepo.addAreas(mCallback1, asList(area1), 0);
+ mRepo.addAreas(mCallback1, asList(area2), 0);
+
+ mRepo.removeAreas(mCallback1, asList(area1, area2), 0);
+ assertFalse(mRepo.isCallbackAvailable(mCallback1));
+
+ mRepo.addAreas(mCallback1, asList(area1), 0);
+ assertTrue(mRepo.isCallbackAvailable(mCallback1));
+ }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 339a5f9..7020744 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -110,6 +110,7 @@
data: [
":JobTestApp",
+ ":StubTestApp",
],
java_resources: [
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 5a0f1ee..bb3eb81 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -16,6 +16,13 @@
<configuration description="Runs Frameworks Services Tests.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="SimpleServiceTestApp3.apk"
+ value="/data/local/tmp/cts/content/SimpleServiceTestApp3.apk" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
@@ -28,6 +35,17 @@
<option name="test-file-name" value="SimpleServiceTestApp3.apk" />
</target_preparer>
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/servicestests" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/servicestests"/>
+ </target_preparer>
+
+ <!-- Load additional APKs onto device -->
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="StubTestApp.apk->/data/local/tmp/servicestests/StubTestApp.apk"/>
+ </target_preparer>
+
<option name="test-tag" value="FrameworksServicesTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.servicestests" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index 8b6b7c2..1d6ed03 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -296,14 +296,6 @@
}
@Test
- public void testToggleSplitScreen_legacy() {
- setupWithRealContext();
- mSystemActionPerformer.performSystemAction(
- AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
- verify(mMockStatusBarManagerInternal).toggleSplitScreen();
- }
-
- @Test
public void testScreenshot_requestsFromScreenshotHelper_legacy() {
setupWithMockContext();
mSystemActionPerformer.performSystemAction(
diff --git a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
index 10f4c05..e6a8dea 100644
--- a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -26,7 +27,9 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
+import android.os.IBinder;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
@@ -42,6 +45,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Build/Install/Run:
@@ -69,6 +74,12 @@
private static final int ACTION_STOPPKG = 8;
private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG;
+ private static final String LOCAL_APK_BASE_PATH = "/data/local/tmp/cts/content/";
+ private static final String TEST_PACKAGE3_APK = "SimpleServiceTestApp3.apk";
+ private static final String ACTION_SERVICE_WITH_DEP_PKG =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
+ private static final String EXTRA_TARGET_PACKAGE = "target_package";
+
private Context mContext;
private Instrumentation mInstrumentation;
private int mTestPackage1Uid;
@@ -199,6 +210,83 @@
return res;
}
+ @Test
+ public void testServiceWithDepPkgStopped() throws Exception {
+ final CountDownLatch[] latchHolder = new CountDownLatch[1];
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ latchHolder[0].countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ latchHolder[0].countDown();
+ }
+ };
+
+ final long timeout = 5_000;
+ final long shortTimeout = 2_000;
+ final Intent intent = new Intent(ACTION_SERVICE_WITH_DEP_PKG);
+ final String testPkg = TEST_PACKAGE2_NAME;
+ final String libPkg = TEST_PACKAGE3_NAME;
+ final String apkPath = LOCAL_APK_BASE_PATH + TEST_PACKAGE3_APK;
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+
+ intent.setComponent(ComponentName.unflattenFromString(testPkg + "/" + TEST_SERVICE_NAME));
+ intent.putExtra(EXTRA_TARGET_PACKAGE, libPkg);
+ try {
+ executeShellCmd("am service-restart-backoff disable " + testPkg);
+
+ latchHolder[0] = new CountDownLatch(1);
+ assertTrue("Unable to bind to test service in " + testPkg,
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE));
+ assertTrue("Timed out to bind service in " + testPkg,
+ latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(shortTimeout);
+ assertTrue(libPkg + " should be a dependency package of " + testPkg,
+ isPackageDependency(testPkg, libPkg));
+
+ // Force-stop lib package, the test service should be restarted.
+ latchHolder[0] = new CountDownLatch(2);
+ am.forceStopPackage(libPkg);
+ assertTrue("Test service in didn't restart in " + testPkg,
+ latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(shortTimeout);
+
+ // Re-install the lib package, the test service should be restarted.
+ latchHolder[0] = new CountDownLatch(2);
+ assertTrue("Unable to install package " + apkPath, installPackage(apkPath));
+ assertTrue("Test service in didn't restart in " + testPkg,
+ latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(shortTimeout);
+
+ // Force-stop the service package, the test service should not be restarted.
+ latchHolder[0] = new CountDownLatch(2);
+ am.forceStopPackage(testPkg);
+ assertFalse("Test service should not be restarted in " + testPkg,
+ latchHolder[0].await(timeout * 2, TimeUnit.MILLISECONDS));
+ } finally {
+ executeShellCmd("am service-restart-backoff enable " + testPkg);
+ mContext.unbindService(conn);
+ am.forceStopPackage(testPkg);
+ }
+ }
+
+ private boolean isPackageDependency(String pkgName, String libPackage) throws Exception {
+ final String output = SystemUtil.runShellCommand("dumpsys activity processes " + pkgName);
+ final Matcher matcher = Pattern.compile("packageDependencies=\\{.*?\\b"
+ + libPackage + "\\b.*?\\}").matcher(output);
+ return matcher.find();
+ }
+
+ private boolean installPackage(String apkPath) throws Exception {
+ return executeShellCmd("pm install -t " + apkPath).equals("Success\n");
+ }
+
private void startServiceAndWait(String pkgName, MyUidImportanceListener uidListener,
long timeout) throws Exception {
final Intent intent = new Intent();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index b3f7587..b255a35 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -302,6 +302,65 @@
testInvokesCancel(session -> session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null));
}
+ // TODO (b/208484275) : Enable these tests
+ // @Test
+ // public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception {
+ // SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
+ // when(manager
+ // .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
+ // .thenReturn(false);
+ // when(mContext.getSystemService(SensorPrivacyManager.class))
+ // .thenReturn(manager);
+ // setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+ // mock(IBiometricAuthenticator.class));
+ // final PromptInfo promptInfo = createPromptInfo(Authenticators.BIOMETRIC_STRONG);
+ // final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
+ // assertEquals(BiometricManager.BIOMETRIC_SUCCESS, preAuthInfo.getCanAuthenticateResult());
+ // for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
+ // assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
+ // }
+ // }
+
+ // @Test
+ // public void testPreAuth_cannotAuthAndPrivacyEnabled() throws Exception {
+ // SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
+ // when(manager
+ // .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
+ // .thenReturn(true);
+ // when(mContext.getSystemService(SensorPrivacyManager.class))
+ // .thenReturn(manager);
+ // setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+ // mock(IBiometricAuthenticator.class));
+ // final PromptInfo promptInfo = createPromptInfo(Authenticators.BIOMETRIC_STRONG);
+ // final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
+ // assertEquals(BiometricManager.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED,
+ // preAuthInfo.getCanAuthenticateResult());
+ // // Even though canAuth returns privacy enabled, we should still be able to authenticate.
+ // for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
+ // assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
+ // }
+ // }
+
+ // @Test
+ // public void testPreAuth_canAuthAndPrivacyEnabledCredentialEnabled() throws Exception {
+ // SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
+ // when(manager
+ // .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
+ // .thenReturn(true);
+ // when(mContext.getSystemService(SensorPrivacyManager.class))
+ // .thenReturn(manager);
+ // setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+ // mock(IBiometricAuthenticator.class));
+ // final PromptInfo promptInfo =
+ // createPromptInfo(Authenticators.BIOMETRIC_STRONG
+ // | Authenticators. DEVICE_CREDENTIAL);
+ // final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
+ // assertEquals(BiometricManager.BIOMETRIC_SUCCESS, preAuthInfo.getCanAuthenticateResult());
+ // for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
+ // assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
+ // }
+ // }
+
private void testInvokesCancel(Consumer<AuthSession> sessionConsumer) throws RemoteException {
final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
@@ -331,7 +390,8 @@
userId,
promptInfo,
TEST_PACKAGE,
- checkDevicePolicyManager);
+ checkDevicePolicyManager,
+ mContext);
}
private AuthSession createAuthSession(List<BiometricSensor> sensors,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index e3e3900..d192697 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -143,8 +143,8 @@
final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
- final BiometricPromptClientMonitor client1 =
- new BiometricPromptClientMonitor(mContext, mToken, lazyDaemon1, listener1);
+ final TestAuthenticationClient client1 =
+ new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
@@ -180,8 +180,8 @@
@Test
public void testCancelNotInvoked_whenOperationWaitingForCookie() {
final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class);
- final BiometricPromptClientMonitor client1 = new BiometricPromptClientMonitor(mContext,
- mToken, lazyDaemon1, mock(ClientMonitorCallbackConverter.class));
+ final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
+ lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class));
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
// Schedule a BiometricPrompt authentication request
@@ -195,6 +195,8 @@
// should go back to idle, since in this case the framework has not even requested the HAL
// to authenticate yet.
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+ assertTrue(client1.isAlreadyDone());
+ assertTrue(client1.mDestroyed);
assertNull(mScheduler.mCurrentOperation);
}
@@ -316,6 +318,10 @@
eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
eq(0) /* vendorCode */);
assertNull(mScheduler.getCurrentClient());
+ assertTrue(client1.isAlreadyDone());
+ assertTrue(client1.mDestroyed);
+ assertTrue(client2.isAlreadyDone());
+ assertTrue(client2.mDestroyed);
}
@Test
@@ -465,39 +471,9 @@
return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
}
- private static class BiometricPromptClientMonitor extends AuthenticationClient<Object> {
-
- public BiometricPromptClientMonitor(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, ClientMonitorCallbackConverter listener) {
- super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
- false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
- TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */,
- 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class),
- false /* isKeyguard */, true /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
- }
-
- @Override
- protected void stopHalOperation() {
- }
-
- @Override
- protected void startHalOperation() {
- }
-
- @Override
- protected void handleLifecycleAfterAuth(boolean authenticated) {
-
- }
-
- @Override
- public boolean wasUserDetected() {
- return false;
- }
- }
-
private static class TestAuthenticationClient extends AuthenticationClient<Object> {
int mNumCancels = 0;
+ boolean mDestroyed = false;
public TestAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -530,6 +506,13 @@
return false;
}
+ @Override
+ public void destroy() {
+ mDestroyed = true;
+ super.destroy();
+ }
+
+ @Override
public void cancel() {
mNumCancels++;
super.cancel();
@@ -595,6 +578,7 @@
@Override
public void destroy() {
+ super.destroy();
mDestroyed = true;
}
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 cadc816..97fb399 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -102,6 +102,7 @@
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Process;
@@ -2102,9 +2103,12 @@
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
- setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID, null,
+ Build.VERSION_CODES.Q);
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
boolean originalCameraDisabled = dpm.getCameraDisabled(admin1);
assertExpectException(SecurityException.class, /* messageRegex= */ null,
() -> dpm.setCameraDisabled(admin1, true));
@@ -2674,8 +2678,8 @@
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
// Test 1. Caller doesn't have DO or DA.
- assertExpectException(SecurityException.class, /* messageRegex= */ "No active admin",
- () -> dpm.getWifiMacAddress(admin1));
+ assertExpectException(SecurityException.class, /* messageRegex= */
+ "does not exist or is not owned by uid", () -> dpm.getWifiMacAddress(admin1));
// DO needs to be an DA.
dpm.setActiveAdmin(admin1, /* replace =*/ false);
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 03eba9b..ee0723b 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -18,8 +18,6 @@
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
@@ -183,10 +181,8 @@
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
- assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
- OTHER_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new DeviceState[]{DEFAULT_DEVICE_STATE});
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
flushHandler();
// The current committed and requests states do not change because the current state remains
@@ -194,10 +190,9 @@
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
- assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE);
assertArrayEquals(callback.getLastNotifiedInfo().supportedStates,
- new int[]{DEFAULT_DEVICE_STATE.getIdentifier()});
+ new int[] { DEFAULT_DEVICE_STATE.getIdentifier() });
}
@Test
@@ -212,11 +207,9 @@
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
- assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
- OTHER_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new DeviceState[]{DEFAULT_DEVICE_STATE,
- OTHER_DEVICE_STATE});
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE,
+ OTHER_DEVICE_STATE });
flushHandler();
// The current committed and requests states do not change because the current state remains
@@ -224,8 +217,6 @@
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
- assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
- OTHER_DEVICE_STATE);
// The callback wasn't notified about a change in supported states as the states have not
// changed.
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index fbc1952..8279624 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -16,17 +16,12 @@
package com.android.server.display;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.DEFAULT_DISPLAY_GROUP;
-
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED;
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED;
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.eq;
@@ -60,7 +55,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
-import java.util.Set;
@SmallTest
@Presubmit
@@ -129,14 +123,14 @@
// add
LogicalDisplay displayAdded = add(device);
assertEquals(info(displayAdded).address, info(device).address);
- assertEquals(DEFAULT_DISPLAY, id(displayAdded));
+ assertEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
// remove
mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
verify(mListenerMock).onLogicalDisplayEventLocked(
mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
- assertEquals(DEFAULT_DISPLAY, id(displayRemoved));
+ assertEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
assertEquals(displayAdded, displayRemoved);
}
@@ -161,11 +155,11 @@
LogicalDisplay display1 = add(device1);
assertEquals(info(display1).address, info(device1).address);
- assertNotEquals(DEFAULT_DISPLAY, id(display1));
+ assertNotEquals(Display.DEFAULT_DISPLAY, id(display1));
LogicalDisplay display2 = add(device2);
assertEquals(info(display2).address, info(device2).address);
- assertEquals(DEFAULT_DISPLAY, id(display2));
+ assertEquals(Display.DEFAULT_DISPLAY, id(display2));
}
@Test
@@ -177,12 +171,12 @@
LogicalDisplay display1 = add(device1);
assertEquals(info(display1).address, info(device1).address);
- assertEquals(DEFAULT_DISPLAY, id(display1));
+ assertEquals(Display.DEFAULT_DISPLAY, id(display1));
LogicalDisplay display2 = add(device2);
assertEquals(info(display2).address, info(device2).address);
// Despite the flags, we can only have one default display
- assertNotEquals(DEFAULT_DISPLAY, id(display2));
+ assertNotEquals(Display.DEFAULT_DISPLAY, id(display2));
}
@Test
@@ -195,67 +189,7 @@
int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
assertEquals(3, ids.length);
Arrays.sort(ids);
- assertEquals(DEFAULT_DISPLAY, ids[0]);
- }
-
- @Test
- public void testGetDisplayInfoForStateLocked_oneDisplayGroup_internalType() {
- add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
- DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
- add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
- DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
- add(createDisplayDevice(Display.TYPE_INTERNAL, 700, 800,
- DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
-
- Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
- DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
- assertThat(displayInfos.size()).isEqualTo(3);
- for (DisplayInfo displayInfo : displayInfos) {
- assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
- assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
- assertThat(displayInfo.logicalWidth).isAnyOf(600, 200, 700);
- assertThat(displayInfo.logicalHeight).isEqualTo(800);
- }
- }
-
- @Test
- public void testGetDisplayInfoForStateLocked_oneDisplayGroup_differentTypes() {
- add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
- DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
- add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
- DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
- add(createDisplayDevice(Display.TYPE_EXTERNAL, 700, 800,
- DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
-
- Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
- DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
- assertThat(displayInfos.size()).isEqualTo(2);
- for (DisplayInfo displayInfo : displayInfos) {
- assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
- assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
- assertThat(displayInfo.logicalWidth).isAnyOf(600, 200);
- assertThat(displayInfo.logicalHeight).isEqualTo(800);
- }
- }
-
- @Test
- public void testGetDisplayInfoForStateLocked_multipleDisplayGroups_defaultGroup() {
- add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
- DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
- add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
- DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
- add(createDisplayDevice(Display.TYPE_VIRTUAL, 700, 800,
- DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP));
-
- Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
- DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
- assertThat(displayInfos.size()).isEqualTo(2);
- for (DisplayInfo displayInfo : displayInfos) {
- assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
- assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
- assertThat(displayInfo.logicalWidth).isAnyOf(600, 200);
- assertThat(displayInfo.logicalHeight).isEqualTo(800);
- }
+ assertEquals(Display.DEFAULT_DISPLAY, ids[0]);
}
@Test
@@ -265,11 +199,11 @@
LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0));
LogicalDisplay display3 = add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
- assertEquals(DEFAULT_DISPLAY_GROUP,
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
- assertEquals(DEFAULT_DISPLAY_GROUP,
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
- assertEquals(DEFAULT_DISPLAY_GROUP,
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
@@ -284,11 +218,11 @@
DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
LogicalDisplay display3 = add(device3);
- assertEquals(DEFAULT_DISPLAY_GROUP,
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
- assertEquals(DEFAULT_DISPLAY_GROUP,
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
- assertNotEquals(DEFAULT_DISPLAY_GROUP,
+ assertNotEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
// Now switch it back to the default group by removing the flag and issuing an update
@@ -297,7 +231,7 @@
mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
// Verify the new group is correct.
- assertEquals(DEFAULT_DISPLAY_GROUP,
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
@@ -353,14 +287,14 @@
// add
LogicalDisplay displayAdded = add(device);
assertEquals(info(displayAdded).address, info(device).address);
- assertNotEquals(DEFAULT_DISPLAY, id(displayAdded));
+ assertNotEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
// remove
mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
verify(mListenerMock).onLogicalDisplayEventLocked(
mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
- assertNotEquals(DEFAULT_DISPLAY, id(displayRemoved));
+ assertNotEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 976a588..18992ec 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
@@ -27,9 +29,12 @@
import static java.lang.reflect.Modifier.isStatic;
import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.content.IIntentReceiver;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
+import android.os.UserHandle;
import android.util.SparseArray;
import androidx.test.runner.AndroidJUnit4;
@@ -62,8 +67,18 @@
// bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest
@RunWith(AndroidJUnit4.class)
public class PackageManagerServiceTest {
+
+ private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
+
+ private static final String TEST_DATA_PATH = "/data/local/tmp/servicestests/";
+ private static final String TEST_APP_APK = "StubTestApp.apk";
+ private static final String TEST_PKG_NAME = "com.android.servicestests.apps.stubapp";
+
+ private IPackageManager mIPackageManager;
+
@Before
public void setUp() throws Exception {
+ mIPackageManager = AppGlobals.getPackageManager();
}
@After
@@ -599,4 +614,26 @@
Collections.sort(knownPackageIds);
return knownPackageIds;
}
+
+ @Test
+ public void testSetSplashScreenTheme_samePackage_succeeds() throws Exception {
+ mIPackageManager.setSplashScreenTheme(PACKAGE_NAME, null /* themeName */,
+ UserHandle.myUserId());
+ // Invoking setSplashScreenTheme on the same package shouldn't get any exception.
+ }
+
+ @Test
+ public void testSetSplashScreenTheme_differentPackage_fails() throws Exception {
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ try {
+ runShellCommand("pm install " + testApk);
+ mIPackageManager.setSplashScreenTheme(TEST_PKG_NAME, null /* themeName */,
+ UserHandle.myUserId());
+ fail("setSplashScreenTheme did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java
new file mode 100644
index 0000000..38297bf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(AndroidJUnit4.class)
+public class UptimeTimerTest {
+ private static final String TAG = "UptimeTimerTest";
+
+ @Test
+ public void testBasic() throws InterruptedException {
+ AtomicBoolean taskRan = new AtomicBoolean(false);
+ UptimeTimer timer = new UptimeTimer("TestTimer");
+ timer.createTask(() -> taskRan.set(true), 100);
+ Thread.sleep(50);
+ boolean before = taskRan.get();
+ Thread.sleep(100);
+ boolean after = taskRan.get();
+ assertFalse(before);
+ assertTrue(after);
+ }
+
+ @Test
+ public void testCancel() throws InterruptedException {
+ AtomicBoolean taskRan = new AtomicBoolean(false);
+ UptimeTimer timer = new UptimeTimer("TestTimer");
+ UptimeTimer.Task task = timer.createTask(() -> taskRan.set(true), 100);
+ Thread.sleep(50);
+ boolean before = taskRan.get();
+ task.cancel();
+ Thread.sleep(100);
+ boolean after = taskRan.get();
+ assertFalse(before);
+ assertFalse(after);
+ }
+}
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
index 78afb7b..3cc105e 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.android.servicestests.apps.simpleservicetestapp">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<service android:name=".SimpleService"
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
index 4e981b2..b8654d7 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
@@ -17,8 +17,10 @@
import android.app.Service;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -33,6 +35,9 @@
private static final String TEST_CLASS =
"com.android.servicestests.apps.simpleservicetestapp.SimpleService";
+ private static final String ACTION_SERVICE_WITH_DEP_PKG =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
+
private static final String EXTRA_CALLBACK = "callback";
private static final String EXTRA_COMMAND = "command";
private static final String EXTRA_FLAGS = "flags";
@@ -118,6 +123,21 @@
@Override
public IBinder onBind(Intent intent) {
+ if (ACTION_SERVICE_WITH_DEP_PKG.equals(intent.getAction())) {
+ final String targetPkg = intent.getStringExtra(EXTRA_TARGET_PACKAGE);
+ Log.i(TAG, "SimpleService.onBind: " + ACTION_SERVICE_WITH_DEP_PKG + " " + targetPkg);
+ if (targetPkg != null) {
+ Context pkgContext = null;
+ try {
+ pkgContext = createPackageContext(targetPkg,
+ Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to create package context for " + pkgContext, e);
+ }
+ // This effectively loads the target package as a dependency.
+ pkgContext.getClassLoader();
+ }
+ }
return mBinder;
}
}
diff --git a/services/tests/servicestests/test-apps/StubApp/Android.bp b/services/tests/servicestests/test-apps/StubApp/Android.bp
new file mode 100644
index 0000000..99deb3f
--- /dev/null
+++ b/services/tests/servicestests/test-apps/StubApp/Android.bp
@@ -0,0 +1,37 @@
+// 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 {
+ // 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"],
+}
+
+android_test_helper_app {
+ name: "StubTestApp",
+
+ sdk_version: "current",
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml b/services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml
similarity index 62%
rename from packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml
rename to services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml
index 620dd48..90172e7 100644
--- a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml
+++ b/services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
+<!-- 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.
@@ -14,5 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0, 0 C 0.1217, 0.0462, 0.15, 0.4686, 0.1667, 0.66 C 0.1834, 0.8878, 0.1667, 1, 1, 1" />
\ No newline at end of file
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.stubapp">
+
+ <application android:label="StubTestApp">
+ <activity android:name=".TestActivity"
+ android:exported="true" />
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java
similarity index 76%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java
index 3729c09..0d94676 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package android.window;
+package com.android.servicestests.apps.stubapp;
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+import android.app.Activity;
+
+public class TestActivity extends Activity {
+}
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 0ad119d..c994b41 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -61,10 +61,6 @@
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
-import static com.android.server.notification.NotificationManagerService.ACTION_DISABLE_NAS;
-import static com.android.server.notification.NotificationManagerService.ACTION_ENABLE_NAS;
-import static com.android.server.notification.NotificationManagerService.ACTION_LEARNMORE_NAS;
-
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -325,7 +321,6 @@
@Mock
MultiRateLimiter mToastRateLimiter;
BroadcastReceiver mPackageIntentReceiver;
- BroadcastReceiver mNASIntentReceiver;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -553,14 +548,9 @@
&& filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED)
&& filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) {
mPackageIntentReceiver = broadcastReceivers.get(i);
- } else if (filter.hasAction(ACTION_ENABLE_NAS)
- && filter.hasAction(ACTION_DISABLE_NAS)
- && filter.hasAction(ACTION_LEARNMORE_NAS)) {
- mNASIntentReceiver = broadcastReceivers.get(i);
}
}
assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
- assertNotNull("nas intent receiver should exist", mNASIntentReceiver);
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
@@ -655,16 +645,6 @@
mPackageIntentReceiver.onReceive(getContext(), intent);
}
- private void simulateNASUpgradeBroadcast(String action, int uid) {
- final Bundle extras = new Bundle();
- extras.putInt(Intent.EXTRA_USER_ID, uid);
-
- final Intent intent = new Intent(action);
- intent.putExtras(extras);
-
- mNASIntentReceiver.onReceive(getContext(), intent);
- }
-
private ArrayMap<Boolean, ArrayList<ComponentName>> generateResetComponentValues() {
ArrayMap<Boolean, ArrayList<ComponentName>> changed = new ArrayMap<>();
changed.put(true, new ArrayList<>());
@@ -6042,7 +6022,7 @@
}
@Test
- public void testNASSettingUpgrade_userSetNull_noOnBoarding() throws RemoteException {
+ public void testNASSettingUpgrade_userSetNull() throws RemoteException {
ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component1");
TestableNotificationManagerService service = spy(mService);
int userId = 11;
@@ -6055,14 +6035,13 @@
.thenReturn(new ArrayList<>());
when(mAssistants.hasUserSet(userId)).thenReturn(true);
- service.migrateDefaultNASShowNotificationIfNecessary();
+ service.migrateDefaultNAS();
assertTrue(service.isNASMigrationDone(userId));
- verify(service, times(0)).createNASUpgradeNotification(eq(userId));
verify(mAssistants, times(1)).clearDefaults();
}
@Test
- public void testNASSettingUpgrade_userSetSameDefault_noOnBoarding() throws RemoteException {
+ public void testNASSettingUpgrade_userSet() throws RemoteException {
ComponentName defaultComponent = ComponentName.unflattenFromString("package/Component1");
TestableNotificationManagerService service = spy(mService);
int userId = 11;
@@ -6075,55 +6054,10 @@
.thenReturn(new ArrayList(Arrays.asList(defaultComponent)));
when(mAssistants.hasUserSet(userId)).thenReturn(true);
- service.migrateDefaultNASShowNotificationIfNecessary();
- assertTrue(service.isNASMigrationDone(userId));
- verify(service, times(0)).createNASUpgradeNotification(eq(userId));
- verify(mAssistants, times(1)).resetDefaultFromConfig();
- }
-
- @Test
- public void testNASSettingUpgrade_userSetDifferentDefault_showOnboarding()
- throws RemoteException {
- ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
- ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component2");
- TestableNotificationManagerService service = spy(mService);
- int userId = 11;
- setUsers(new int[]{userId});
- setNASMigrationDone(false, userId);
- when(mAssistants.getDefaultComponents())
- .thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
- when(mAssistants.getDefaultFromConfig())
- .thenReturn(newDefaultComponent);
- when(mAssistants.getAllowedComponents(anyInt()))
- .thenReturn(Arrays.asList(oldDefaultComponent));
- when(mAssistants.hasUserSet(userId)).thenReturn(true);
-
- service.migrateDefaultNASShowNotificationIfNecessary();
- assertFalse(service.isNASMigrationDone(userId));
- //TODO(b/192450820)
- //verify(service, times(1)).createNASUpgradeNotification(eq(userId));
- verify(mAssistants, times(0)).resetDefaultFromConfig();
-
- //Test user clear data before enable/disable from onboarding notification
- ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
- generateResetComponentValues();
- when(mListeners.resetComponents(anyString(), anyInt())).thenReturn(changedListeners);
- ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
- changes.put(true, new ArrayList(Arrays.asList(newDefaultComponent)));
- changes.put(false, new ArrayList());
- when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changes);
-
- //Clear data
- service.getBinderService().clearData("package", userId, false);
- //Test migrate flow again
- service.migrateDefaultNASShowNotificationIfNecessary();
-
- //The notification should be still there
- assertFalse(service.isNASMigrationDone(userId));
- //TODO(b/192450820)
- //verify(service, times(2)).createNASUpgradeNotification(eq(userId));
- verify(mAssistants, times(0)).resetDefaultFromConfig();
- assertEquals(oldDefaultComponent, service.getApprovedAssistant(userId));
+ service.migrateDefaultNAS();
+ verify(mAssistants, times(1)).setUserSet(userId, false);
+ //resetDefaultAssistantsIfNecessary should invoke from readPolicyXml() and migration
+ verify(mAssistants, times(2)).resetDefaultAssistantsIfNecessary();
}
@Test
@@ -6143,24 +6077,22 @@
.thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
when(mAssistants.getDefaultFromConfig())
.thenReturn(newDefaultComponent);
- //User1: need onboarding
+ //User1: set different NAS
when(mAssistants.getAllowedComponents(userId1))
.thenReturn(Arrays.asList(oldDefaultComponent));
- //User2: no onboarding
+ //User2: set to none
when(mAssistants.getAllowedComponents(userId2))
- .thenReturn(Arrays.asList(newDefaultComponent));
+ .thenReturn(new ArrayList<>());
when(mAssistants.hasUserSet(userId1)).thenReturn(true);
when(mAssistants.hasUserSet(userId2)).thenReturn(true);
- service.migrateDefaultNASShowNotificationIfNecessary();
- assertFalse(service.isNASMigrationDone(userId1));
+ service.migrateDefaultNAS();
+ // user1's setting get reset
+ verify(mAssistants, times(1)).setUserSet(userId1, false);
+ verify(mAssistants, times(0)).setUserSet(eq(userId2), anyBoolean());
assertTrue(service.isNASMigrationDone(userId2));
- //TODO(b/192450820)
- //verify(service, times(1)).createNASUpgradeNotification(any(Integer.class));
- // only user2's default get updated
- verify(mAssistants, times(1)).resetDefaultFromConfig();
}
@Test
@@ -6180,7 +6112,7 @@
.thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
when(mAssistants.getDefaultFromConfig())
.thenReturn(newDefaultComponent);
- //Both profiles: need onboarding
+ //Both profiles: set different NAS
when(mAssistants.getAllowedComponents(userId1))
.thenReturn(Arrays.asList(oldDefaultComponent));
when(mAssistants.getAllowedComponents(userId2))
@@ -6189,13 +6121,9 @@
when(mAssistants.hasUserSet(userId1)).thenReturn(true);
when(mAssistants.hasUserSet(userId2)).thenReturn(true);
- service.migrateDefaultNASShowNotificationIfNecessary();
+ service.migrateDefaultNAS();
assertFalse(service.isNASMigrationDone(userId1));
assertFalse(service.isNASMigrationDone(userId2));
-
- // TODO(b/192450820): only user1 get notification
- //verify(service, times(1)).createNASUpgradeNotification(eq(userId1));
- //verify(service, times(0)).createNASUpgradeNotification(eq(userId2));
}
@@ -6223,79 +6151,16 @@
//Clear data
service.getBinderService().clearData("package", userId, false);
//Test migrate flow again
- service.migrateDefaultNASShowNotificationIfNecessary();
+ service.migrateDefaultNAS();
- //TODO(b/192450820): The notification should not appear again
- //verify(service, times(0)).createNASUpgradeNotification(eq(userId));
- verify(mAssistants, times(0)).resetDefaultFromConfig();
+ //Migration should not happen again
+ verify(mAssistants, times(0)).setUserSet(userId, false);
+ verify(mAssistants, times(0)).clearDefaults();
+ //resetDefaultAssistantsIfNecessary should only invoke once from readPolicyXml()
+ verify(mAssistants, times(1)).resetDefaultAssistantsIfNecessary();
+
}
- @Test
- public void testNASUpgradeNotificationDisableBroadcast_multiProfile() {
- int userId1 = 11;
- int userId2 = 12;
- setUsers(new int[]{userId1, userId2});
- when(mUm.isManagedProfile(userId2)).thenReturn(true);
- when(mUm.getProfileIds(userId1, false)).thenReturn(new int[]{userId1, userId2});
-
- TestableNotificationManagerService service = spy(mService);
- setNASMigrationDone(false, userId1);
- setNASMigrationDone(false, userId2);
-
- simulateNASUpgradeBroadcast(ACTION_DISABLE_NAS, userId1);
-
- assertTrue(service.isNASMigrationDone(userId1));
- assertTrue(service.isNASMigrationDone(userId2));
- // User disabled the NAS from notification, the default stored in xml should be null
- // rather than the new default
- verify(mAssistants, times(1)).clearDefaults();
- verify(mAssistants, times(0)).resetDefaultFromConfig();
-
- //TODO(b/192450820):No more notification after disabled
- //service.migrateDefaultNASShowNotificationIfNecessary();
- //verify(service, times(0)).createNASUpgradeNotification(anyInt());
- }
-
- @Test
- public void testNASUpgradeNotificationEnableBroadcast_multiUser() {
- int userId1 = 11;
- int userId2 = 12;
- setUsers(new int[]{userId1, userId2});
- when(mUm.getProfileIds(userId1, false)).thenReturn(new int[]{userId1});
-
- TestableNotificationManagerService service = spy(mService);
- setNASMigrationDone(false, userId1);
- setNASMigrationDone(false, userId2);
-
- simulateNASUpgradeBroadcast(ACTION_ENABLE_NAS, userId1);
-
- assertTrue(service.isNASMigrationDone(userId1));
- assertFalse(service.isNASMigrationDone(userId2));
- verify(mAssistants, times(1)).resetDefaultFromConfig();
-
- //TODO(b/192450820)
- //service.migrateDefaultNASShowNotificationIfNecessary();
- //verify(service, times(0)).createNASUpgradeNotification(eq(userId1));
- }
-
- @Test
- public void testNASUpgradeNotificationLearnMoreBroadcast() {
- int userId = 11;
- setUsers(new int[]{userId});
- TestableNotificationManagerService service = spy(mService);
- setNASMigrationDone(false, userId);
- doNothing().when(mContext).startActivity(any());
-
- simulateNASUpgradeBroadcast(ACTION_LEARNMORE_NAS, userId);
-
- verify(mContext, times(1)).startActivity(any(Intent.class));
- assertFalse(service.isNASMigrationDone(userId));
- //TODO(b/192450820)
- //verify(service, times(0)).createNASUpgradeNotification(eq(userId));
- verify(mAssistants, times(0)).resetDefaultFromConfig();
- }
-
-
private void setNASMigrationDone(boolean done, int userId) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.NAS_SETTINGS_UPDATED, done ? 1 : 0, userId);
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 fc2c162..a9a603b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -39,6 +43,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -102,6 +107,7 @@
import static org.mockito.Mockito.never;
import android.app.ActivityOptions;
+import android.app.ICompatCameraControlCallback;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.DestroyActivityItem;
@@ -128,6 +134,8 @@
import android.view.IRemoteAnimationRunner.Stub;
import android.view.IWindowManager;
import android.view.IWindowSession;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
@@ -1779,11 +1787,6 @@
anyInt() /* orientation */, anyInt() /* lastRotation */);
// Set to visible so the activity can freeze the screen.
activity.setVisibility(true);
- // Update the display policy to make the screen fully turned on so the freeze is allowed
- display.getDisplayPolicy().screenTurnedOn(null);
- display.getDisplayPolicy().finishKeyguardDrawn();
- display.getDisplayPolicy().finishWindowsDrawn();
- display.getDisplayPolicy().finishScreenTurningOn();
display.rotateInDifferentOrientationIfNeeded(activity);
display.setFixedRotationLaunchingAppUnchecked(activity);
@@ -3043,6 +3046,41 @@
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testImeInsetsFrozenFlag_resetWhenReportedToBeImeInputTarget() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ InsetsSource imeSource = new InsetsSource(ITYPE_IME);
+ app.getInsetsState().addSource(imeSource);
+ mDisplayContent.setImeLayeringTarget(app);
+ mDisplayContent.updateImeInputAndControlTarget(app);
+
+ InsetsState state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(app);
+ assertFalse(state.getSource(ITYPE_IME).isVisible());
+ assertTrue(state.getSource(ITYPE_IME).getFrame().isEmpty());
+
+ // Simulate app is closing and expect IME insets is frozen.
+ mDisplayContent.mOpeningApps.clear();
+ app.mActivityRecord.commitVisibility(false, false);
+ app.mActivityRecord.onWindowsGone();
+ assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+ // Simulate app re-start input or turning screen off/on then unlocked by un-secure
+ // keyguard to back to the app, expect IME insets is not frozen
+ imeSource.setFrame(new Rect(100, 400, 500, 500));
+ app.getInsetsState().addSource(imeSource);
+ app.getInsetsState().setSourceVisible(ITYPE_IME, true);
+ mDisplayContent.updateImeInputAndControlTarget(app);
+ assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+ // Verify when IME is visible and the app can receive the right IME insets from policy.
+ makeWindowVisibleAndDrawn(app, mImeWindow);
+ state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(app);
+ assertTrue(state.getSource(ITYPE_IME).isVisible());
+ assertEquals(state.getSource(ITYPE_IME).getFrame(), imeSource.getFrame());
+ }
+
@Test
public void testInClosingAnimation_doNotHideSurface() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
@@ -3065,6 +3103,188 @@
eq(null));
}
+ @Test
+ public void testUpdateCameraCompatState_flagIsEnabled_controlStateIsUpdated() {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+ }
+
+ @Test
+ public void testUpdateCameraCompatState_flagIsDisabled_controlStateIsHidden() {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being disabled.
+ doReturn(false).when(activity).isCameraCompatControlEnabled();
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnDismiss() throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ verify(callback, never()).revertCameraCompatTreatment();
+ verify(callback, never()).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ // All following updates are ignored.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnApplyTreatment()
+ throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ verify(callback, never()).revertCameraCompatTreatment();
+ verify(callback).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Request from the client to show the control are ignored respecting the user choice.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Request from the client to hide the control is respected.
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Request from the client to show the control again is respected.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnRevertTreatment()
+ throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(callback).revertCameraCompatTreatment();
+ verify(callback, never()).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Request from the client to show the control are ignored respecting the user choice.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Request from the client to hide the control is respected.
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Request from the client to show the control again is respected.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ }
+
+ private ICompatCameraControlCallback getCompatCameraControlCallback() {
+ return new ICompatCameraControlCallback.Stub() {
+ @Override
+ public void applyCameraCompatTreatment() {}
+
+ @Override
+ public void revertCameraCompatTreatment() {}
+ };
+ }
+
private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 5d0e34a..5062706 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -334,6 +334,18 @@
}
@Test
+ public void testExitAnimationDone_beforeAppTransition() {
+ final Task task = createTask(mDisplayContent);
+ final WindowState win = createAppWindow(task, ACTIVITY_TYPE_STANDARD, "Win");
+ spyOn(win);
+ win.mAnimatingExit = true;
+ mDisplayContent.mAppTransition.setTimeout();
+ mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+ verify(win).onExitAnimationDone();
+ }
+
+ @Test
public void testGetAnimationTargets_windowsAreBeingReplaced() {
// [DisplayContent] -+- [Task1] - [ActivityRecord1] (opening, visible)
// +- [AppWindow1] (being-replaced)
@@ -803,6 +815,7 @@
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
openingActivity.allDrawn = true;
+ task.effectiveUid = openingActivity.getUid();
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
@@ -879,6 +892,7 @@
final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
closingActivity.allDrawn = true;
closingActivity.info.applicationInfo.uid = 12345;
+ task.effectiveUid = closingActivity.getUid();
// Opening non-embedded activity with different UID.
final ActivityRecord openingActivity = createActivityRecord(task);
openingActivity.info.applicationInfo.uid = 54321;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index c0959d3..2ea7fda 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -22,6 +22,9 @@
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -92,6 +95,7 @@
final ActivityRecord activity = createActivityRecord(dc);
mDc.prepareAppTransition(TRANSIT_OPEN);
+ mDc.prepareAppTransition(TRANSIT_KEYGUARD_OCCLUDE);
mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
mDc.mOpeningApps.add(activity);
assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
@@ -102,6 +106,22 @@
}
@Test
+ public void testKeyguardUnoccludeOcclude() {
+ final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+ final ActivityRecord activity = createActivityRecord(dc);
+
+ mDc.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE);
+ mDc.prepareAppTransition(TRANSIT_KEYGUARD_OCCLUDE);
+ mDc.mOpeningApps.add(activity);
+ assertEquals(TRANSIT_NONE,
+ AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
+
+ }
+
+ @Test
public void testKeyguardKeep() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
final ActivityRecord activity = createActivityRecord(dc);
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 004e45a..b2b844b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -180,23 +180,23 @@
}
private int getNonDecorDisplayWidth(DisplayInfo di) {
- return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight,
- di.rotation, 0 /* ui */, di.displayCutout);
+ return mDisplayPolicy.getNonDecorDisplaySize(di.logicalWidth, di.logicalHeight,
+ di.rotation, 0 /* ui */, di.displayCutout).x;
}
private int getNonDecorDisplayHeight(DisplayInfo di) {
- return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight,
- di.rotation, 0 /* ui */, di.displayCutout);
+ return mDisplayPolicy.getNonDecorDisplaySize(di.logicalWidth, di.logicalHeight,
+ di.rotation, 0 /* ui */, di.displayCutout).y;
}
private int getConfigDisplayWidth(DisplayInfo di) {
- return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight,
- di.rotation, 0 /* ui */, di.displayCutout);
+ return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
+ di.rotation, 0 /* ui */, di.displayCutout).x;
}
private int getConfigDisplayHeight(DisplayInfo di) {
- return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight,
- di.rotation, 0 /* ui */, di.displayCutout);
+ return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
+ di.rotation, 0 /* ui */, di.displayCutout).y;
}
private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
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 8da8596..6970005 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -189,11 +189,6 @@
assertEquals(0,
displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS, null));
- // Dimming window clears APPEARANCE_LIGHT_NAVIGATION_BARS.
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, dimming));
-
// Control window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar));
assertEquals(0, displayPolicy.updateLightNavigationBarLw(
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index a8ede13..407f9cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_ON;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -29,7 +30,9 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -44,6 +47,7 @@
import com.android.server.inputmethod.InputMethodManagerService;
import com.android.server.inputmethod.InputMethodMenuController;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,18 +66,24 @@
private InputMethodMenuController mController;
private DualDisplayAreaGroupPolicyTest.DualDisplayContent mSecondaryDisplay;
+ private IWindowManager mIWindowManager;
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+
@Before
public void setUp() throws Exception {
- // Let the Display to be created with the DualDisplay policy.
+ // Let the Display be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
+ mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
+ .Builder(mAtm, 1000, 1000).build();
+ mSecondaryDisplay.getDisplayInfo().state = STATE_ON;
// Mock addWindowTokenWithOptions to create a test window token.
- IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
- spyOn(wms);
+ mIWindowManager = WindowManagerGlobal.getWindowManagerService();
+ spyOn(mIWindowManager);
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
IBinder clientToken = (IBinder) args[0];
@@ -83,19 +93,24 @@
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
null /* options */);
return dc.getImeContainer().getConfiguration();
- }).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
- anyInt(), any());
-
- mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
- .Builder(mAtm, 1000, 1000).build();
-
- // Mock DisplayManagerGlobal to return test display when obtaining Display instance.
+ }).when(mIWindowManager).attachWindowContextToDisplayArea(any(),
+ eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any());
+ mDisplayManagerGlobal = DisplayManagerGlobal.getInstance();
+ spyOn(mDisplayManagerGlobal);
final int displayId = mSecondaryDisplay.getDisplayId();
final Display display = mSecondaryDisplay.getDisplay();
- DisplayManagerGlobal displayManagerGlobal = DisplayManagerGlobal.getInstance();
- spyOn(displayManagerGlobal);
- doReturn(display).when(displayManagerGlobal).getCompatibleDisplay(eq(displayId),
+ doReturn(display).when(mDisplayManagerGlobal).getCompatibleDisplay(eq(displayId),
(Resources) any());
+ Context systemUiContext = ActivityThread.currentActivityThread()
+ .getSystemUiContext(displayId);
+ spyOn(systemUiContext);
+ doReturn(display).when(systemUiContext).getDisplay();
+ }
+
+ @After
+ public void tearDown() {
+ reset(mIWindowManager);
+ reset(mDisplayManagerGlobal);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
deleted file mode 100644
index 6e00568..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.FLAG_PRESENTATION;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.when;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.ArraySet;
-import android.view.DisplayInfo;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Set;
-
-
-/**
- * Tests for {@link PossibleDisplayInfoMapper}.
- *
- * Build/Install/Run:
- * atest WmTests:PossibleDisplayInfoMapperTests
- */
-@MediumTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
-
- private PossibleDisplayInfoMapper mDisplayInfoMapper;
- private final Set<DisplayInfo> mPossibleDisplayInfo = new ArraySet<>();
- private DisplayInfo mDefaultDisplayInfo;
- private DisplayInfo mSecondDisplayInfo;
-
- @Before
- public void setUp() throws Exception {
- mDisplayInfoMapper = mWm.mPossibleDisplayInfoMapper;
- final DisplayInfo baseDisplayInfo = mWm.mRoot.getDisplayContent(
- DEFAULT_DISPLAY).getDisplayInfo();
- when(mWm.mDisplayManagerInternal.getPossibleDisplayInfo(anyInt())).thenReturn(
- mPossibleDisplayInfo);
-
- mDefaultDisplayInfo = new DisplayInfo(baseDisplayInfo);
- initializeDisplayInfo(mDefaultDisplayInfo, DEFAULT_DISPLAY, new Rect(0, 0, 500, 800));
- mSecondDisplayInfo = new DisplayInfo(baseDisplayInfo);
- // Use the same display id for any display in the same group, due to the assumption that
- // any display in the same grouped can be swapped out for each other (while maintaining the
- // display id).
- initializeDisplayInfo(mSecondDisplayInfo, DEFAULT_DISPLAY, new Rect(0, 0, 600, 1600));
- mSecondDisplayInfo.flags |= FLAG_PRESENTATION;
- }
-
- @Test
- public void testInitialization_isEmpty() {
- // Empty after initializing.
- assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY)).isEmpty();
-
- // Still empty after updating.
- mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
- assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY)).isEmpty();
- }
-
- @Test
- public void testUpdatePossibleDisplayInfos_singleDisplay() {
- mPossibleDisplayInfo.add(mDefaultDisplayInfo);
- mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
-
- Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
- // An entry for each possible rotation, for a display that can be in a single state.
- assertThat(displayInfos.size()).isEqualTo(4);
- assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
- }
-
- @Test
- public void testUpdatePossibleDisplayInfos_secondDisplayAdded_sameGroup() {
- mPossibleDisplayInfo.add(mDefaultDisplayInfo);
- mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
-
- assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(4);
-
- // Add another display layout to the set of supported states.
- mPossibleDisplayInfo.add(mSecondDisplayInfo);
- mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
-
- Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
- Set<DisplayInfo> defaultDisplayInfos = new ArraySet<>();
- Set<DisplayInfo> secondDisplayInfos = new ArraySet<>();
- for (DisplayInfo di : displayInfos) {
- if ((di.flags & FLAG_PRESENTATION) != 0) {
- secondDisplayInfos.add(di);
- } else {
- defaultDisplayInfos.add(di);
- }
- }
- // An entry for each possible rotation, for the default display.
- assertThat(defaultDisplayInfos).hasSize(4);
- assertPossibleDisplayInfoEntries(defaultDisplayInfos, mDefaultDisplayInfo);
-
- // An entry for each possible rotation, for the second display.
- assertThat(secondDisplayInfos).hasSize(4);
- assertPossibleDisplayInfoEntries(secondDisplayInfos, mSecondDisplayInfo);
- }
-
- @Test
- public void testUpdatePossibleDisplayInfos_secondDisplayAdded_differentGroup() {
- mPossibleDisplayInfo.add(mDefaultDisplayInfo);
- mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
-
- assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(4);
-
- // Add another display to a different group.
- mSecondDisplayInfo.displayId = DEFAULT_DISPLAY + 1;
- mSecondDisplayInfo.displayGroupId = mDefaultDisplayInfo.displayGroupId + 1;
- mPossibleDisplayInfo.add(mSecondDisplayInfo);
- mDisplayInfoMapper.updatePossibleDisplayInfos(mSecondDisplayInfo.displayId);
-
- Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
- // An entry for each possible rotation, for the default display.
- assertThat(displayInfos).hasSize(4);
- assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
-
- Set<DisplayInfo> secondStateEntries =
- mDisplayInfoMapper.getPossibleDisplayInfos(mSecondDisplayInfo.displayId);
- // An entry for each possible rotation, for the second display.
- assertThat(secondStateEntries).hasSize(4);
- assertPossibleDisplayInfoEntries(secondStateEntries, mSecondDisplayInfo);
- }
-
- private static void initializeDisplayInfo(DisplayInfo outDisplayInfo, int displayId,
- Rect logicalBounds) {
- outDisplayInfo.displayId = displayId;
- outDisplayInfo.rotation = ROTATION_0;
- outDisplayInfo.logicalWidth = logicalBounds.width();
- outDisplayInfo.logicalHeight = logicalBounds.height();
- }
-
- private static void assertPossibleDisplayInfoEntries(Set<DisplayInfo> displayInfos,
- DisplayInfo expectedDisplayInfo) {
- boolean[] seenEveryRotation = new boolean[4];
- for (DisplayInfo displayInfo : displayInfos) {
- final int rotation = displayInfo.rotation;
- seenEveryRotation[rotation] = true;
- assertThat(displayInfo.displayId).isEqualTo(expectedDisplayInfo.displayId);
- assertEqualsRotatedDisplayInfo(displayInfo, expectedDisplayInfo);
- }
- assertThat(seenEveryRotation).isEqualTo(new boolean[]{true, true, true, true});
- }
-
- private static void assertEqualsRotatedDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
- if (actual.rotation == ROTATION_0 || actual.rotation == ROTATION_180) {
- assertThat(actual.logicalWidth).isEqualTo(expected.logicalWidth);
- assertThat(actual.logicalHeight).isEqualTo(expected.logicalHeight);
- } else {
- assertThat(actual.logicalWidth).isEqualTo(expected.logicalHeight);
- assertThat(actual.logicalHeight).isEqualTo(expected.logicalWidth);
- }
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index d68edba..cdf6b59 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -84,7 +84,7 @@
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -110,7 +110,7 @@
final Task adjacentRootTask = createTask(
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
- adjacentRootTask.setAdjacentTaskFragment(rootTask);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
taskDisplayArea.setLaunchRootTask(rootTask,
new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -131,7 +131,7 @@
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 786c3ea..dcaf9d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -329,7 +330,7 @@
// Throw exception if the transaction is trying to change a window that is not organized by
// the organizer.
- mTransaction.setAdjacentRoots(mFragmentWindowToken, token2);
+ mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */);
assertThrows(SecurityException.class, () -> {
try {
@@ -352,28 +353,66 @@
}
@Test
- public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
+ public void testApplyTransaction_enforceHierarchyChange_createTaskFragment()
+ throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final int uid = Binder.getCallingUid();
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ final IBinder fragmentToken = new Binder();
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token).build();
mOrganizer.applyTransaction(mTransaction);
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
- final TaskFragmentCreationParams mockParams = mock(TaskFragmentCreationParams.class);
- doReturn(mOrganizerToken).when(mockParams).getOrganizer();
- mTransaction.createTaskFragment(mockParams);
+ mTransaction.createTaskFragment(params);
mTransaction.startActivityInTaskFragment(
mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */);
mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class),
null /* options */);
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
- // testing the security check here.
- assertThrows(IllegalArgumentException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ // Successfully created a TaskFragment.
+ final TaskFragment taskFragment = mAtm.mWindowOrganizerController
+ .getTaskFragment(fragmentToken);
+ assertNotNull(taskFragment);
+ assertEquals(activity.getTask(), taskFragment.getTask());
+ }
+
+ @Test
+ public void testApplyTransaction_createTaskFragment_failForDifferentUid()
+ throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final int uid = Binder.getCallingUid();
+ final IBinder fragmentToken = new Binder();
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token).build();
+ mOrganizer.applyTransaction(mTransaction);
+ mTransaction.createTaskFragment(params);
+
+ // Fail to create TaskFragment when the task uid is different from caller.
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid + 1;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+
+ // Fail to create TaskFragment when the task uid is different from owner activity.
+ activity.info.applicationInfo.uid = uid + 1;
+ activity.getTask().effectiveUid = uid;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+
+ // Successfully created a TaskFragment for same uid.
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
}
@Test
@@ -424,4 +463,26 @@
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
+
+ @Test
+ public void testDeferPendingTaskFragmentEventsOfInvisibleTask() {
+ // Task - TaskFragment - Activity.
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setOrganizer(mOrganizer)
+ .build();
+
+ // Mock the task to invisible
+ doReturn(false).when(task).shouldBeVisible(any());
+
+ // Sending events
+ mController.registerOrganizer(mIOrganizer);
+ taskFragment.mTaskFragmentAppearedSent = true;
+ mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
+ mController.dispatchPendingEvents();
+
+ // Verifies that event was not sent
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 6737b1a..730275c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -16,11 +16,14 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.clearInvocations;
import android.graphics.Rect;
@@ -91,6 +94,7 @@
final Rect endBounds = new Rect(500, 500, 1000, 1000);
mTaskFragment.setBounds(startBounds);
doReturn(true).when(mTaskFragment).isVisible();
+ doReturn(true).when(mTaskFragment).isVisibleRequested();
clearInvocations(mTransaction);
mTaskFragment.setBounds(endBounds);
@@ -108,6 +112,25 @@
verify(mTransaction).setWindowCrop(mLeash, 500, 500);
}
+ @Test
+ public void testNotOkToAnimate_doNotStartChangeTransition() {
+ mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer);
+ final Rect startBounds = new Rect(0, 0, 1000, 1000);
+ final Rect endBounds = new Rect(500, 500, 1000, 1000);
+ mTaskFragment.setBounds(startBounds);
+ doReturn(true).when(mTaskFragment).isVisible();
+ doReturn(true).when(mTaskFragment).isVisibleRequested();
+
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ displayPolicy.screenTurnedOff();
+
+ assertFalse(mTaskFragment.okToAnimate());
+
+ mTaskFragment.setBounds(endBounds);
+
+ verify(mTaskFragment, never()).initializeChangeTransition(any());
+ }
+
/**
* Tests that when a {@link TaskFragmentInfo} is generated from a {@link TaskFragment}, an
* activity that has not yet been attached to a process because it is being initialized but
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 ce568f1..bfaa815 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -59,6 +59,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
@@ -898,22 +899,21 @@
/**
* Test that root activity index is reported correctly when looking for the 'effective root' in
- * case when bottom activity is finishing. Ignore the relinquishing task identity if it's not a
- * system activity even with the FLAG_RELINQUISH_TASK_IDENTITY.
+ * case when bottom activities are relinquishing task identity or finishing.
*/
@Test
public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
- final Task task = getTestTask();
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity0.getTask();
// Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
// one above as finishing.
- final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
activity1.finishing = true;
new ActivityBuilder(mAtm).setTask(task).build();
assertEquals("The first non-finishing activity and non-relinquishing task identity "
- + "must be reported.", task.getChildAt(0), task.getRootActivity(
+ + "must be reported.", task.getChildAt(2), task.getRootActivity(
false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
@@ -933,21 +933,21 @@
}
/**
- * Test that the root activity index is reported correctly when looking for the
- * 'effective root' for the case when all non-system activities have relinquishTaskIdentity set.
+ * Test that the topmost activity index is reported correctly when looking for the
+ * 'effective root' for the case when all activities have relinquishTaskIdentity set.
*/
@Test
public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
- final Task task = getTestTask();
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity0.getTask();
// Set relinquishTaskIdentity for all activities in the task
- final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
- assertEquals("The topmost activity in the task must be reported.", task.getChildAt(0),
- task.getRootActivity(false /*ignoreRelinquishIdentity*/,
- true /*setToBottomIfNone*/));
+ assertEquals("The topmost activity in the task must be reported.",
+ task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
/** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
@@ -1085,14 +1085,14 @@
}
/**
- * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with non-system
- * activity that relinquishes task identity.
+ * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
+ * relinquishes task identity.
*/
@Test
public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
- final Task task = getTestTask();
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity0.getTask();
// Make the current root activity relinquish task identity
- final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
// Add an extra activity on top - this will be the new root
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1101,7 +1101,7 @@
assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
- assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
@@ -1188,6 +1188,46 @@
verify(task).setIntent(eq(activity0));
}
+ /**
+ * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but
+ * another with different uid. This should make the task use the root activity when updating the
+ * intent.
+ */
+ @Test
+ public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() {
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm)
+ .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
+ final Task task = activity0.getTask();
+
+ // Add an extra activity on top
+ new ActivityBuilder(mAtm).setUid(11).setTask(task).build();
+
+ spyOn(task);
+ task.updateEffectiveIntent();
+ verify(task).setIntent(eq(activity0));
+ }
+
+ /**
+ * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity.
+ * This should make the task use the topmost activity when updating the intent.
+ */
+ @Test
+ public void testUpdateEffectiveIntent_relinquishingMultipleActivities() {
+ final ActivityRecord activity0 = new ActivityBuilder(mAtm)
+ .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
+ final Task task = activity0.getTask();
+ // Add an extra activity on top
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+ // Add an extra activity on top
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+ spyOn(task);
+ task.updateEffectiveIntent();
+ verify(task).setIntent(eq(activity2));
+ }
+
@Test
public void testSaveLaunchingStateWhenConfigurationChanged() {
LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
@@ -1365,6 +1405,25 @@
assertNotNull(activity.getTask().getDimmer());
}
+ @Test
+ public void testMoveToFront_moveAdjacentTask() {
+ final Task task1 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final Task task2 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ spyOn(task2);
+
+ task1.setAdjacentTaskFragment(task2, false /* moveTogether */);
+ task1.moveToFront("" /* reason */);
+ verify(task2, never()).moveToFrontInner(anyString(), isNull());
+
+ // Reset adjacent tasks to move together.
+ task1.setAdjacentTaskFragment(null, false /* moveTogether */);
+ task1.setAdjacentTaskFragment(task2, true /* moveTogether */);
+ task1.moveToFront("" /* reason */);
+ verify(task2).moveToFrontInner(anyString(), isNull());
+ }
+
private Task getTestTask() {
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
return task.getBottomMostTask();
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 0e504d3..e0f69d4f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -165,6 +165,11 @@
doReturn(false).when(displayPolicy).hasStatusBar();
doReturn(false).when(newDisplay).supportsSystemDecorations();
}
+ // Update the display policy to make the screen fully turned on so animation is allowed
+ displayPolicy.screenTurnedOn(null /* screenOnListener */);
+ displayPolicy.finishKeyguardDrawn();
+ displayPolicy.finishWindowsDrawn();
+ displayPolicy.finishScreenTurningOn();
if (mStatusBarHeight > 0) {
doReturn(true).when(displayPolicy).hasStatusBar();
doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index e5eba57..646647f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -17,22 +17,35 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_OFF;
+import static android.view.Display.STATE_ON;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.window.WindowProvider.KEY_IS_WINDOW_PROVIDER_SERVICE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.app.IWindowToken;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
@@ -55,12 +68,15 @@
private static final int ANOTHER_UID = 1000;
private final IBinder mClientToken = new Binder();
- private WindowContainer mContainer;
+ private WindowContainer<?> mContainer;
@Before
public void setUp() {
mController = new WindowContextListenerController();
mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+ // Make display on to verify configuration propagation.
+ mDefaultDisplay.getDisplayInfo().state = STATE_ON;
+ mDisplayContent.getDisplayInfo().state = STATE_ON;
}
@Test
@@ -76,7 +92,7 @@
assertEquals(2, mController.mListeners.size());
- final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDefaultDisplay);
mController.registerWindowContainerListener(mClientToken, container, -1,
TYPE_APPLICATION_OVERLAY, null /* options */);
@@ -89,6 +105,7 @@
assertEquals(container, listener.getWindowContainer());
}
+ @UseTestDisplay
@Test
public void testRegisterWindowContextListenerClientConfigPropagation() {
final TestWindowTokenClient clientToken = new TestWindowTokenClient();
@@ -107,7 +124,7 @@
assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId);
// Update the WindowContainer.
- final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDefaultDisplay);
final Configuration config2 = container.getConfiguration();
final Rect bounds2 = new Rect(0, 0, 20, 20);
@@ -174,7 +191,7 @@
.setDisplayContent(mDefaultDisplay)
.setFromClientToken(true)
.build();
- final DisplayArea da = windowContextCreatedToken.getDisplayArea();
+ final DisplayArea<?> da = windowContextCreatedToken.getDisplayArea();
mController.registerWindowContainerListener(mClientToken, windowContextCreatedToken,
TEST_UID, TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
@@ -192,11 +209,12 @@
// Let the Display to be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
- Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
+ doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
// Create a DisplayContent with dual RootDisplayArea
DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent =
new DualDisplayAreaGroupPolicyTest.DualDisplayContent
.Builder(mAtm, 1000, 1000).build();
+ dualDisplayContent.getDisplayInfo().state = STATE_ON;
final DisplayArea.Tokens imeContainer = dualDisplayContent.getImeContainer();
// Put the ImeContainer to the first sub-RootDisplayArea
dualDisplayContent.mFirstRoot.placeImeContainer(imeContainer);
@@ -222,7 +240,62 @@
assertThat(mController.getContainer(mClientToken)).isEqualTo(imeContainer);
}
- private class TestWindowTokenClient extends IWindowToken.Stub {
+ @Test
+ public void testConfigUpdateForSuspendedWindowContext() {
+ final TestWindowTokenClient mockToken = new TestWindowTokenClient();
+ spyOn(mockToken);
+
+ mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;
+
+ final Configuration config1 = mContainer.getConfiguration();
+ final Rect bounds1 = new Rect(0, 0, 10, 10);
+ config1.windowConfiguration.setBounds(bounds1);
+ config1.densityDpi = 100;
+ mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+ mController.registerWindowContainerListener(mockToken, mContainer, -1,
+ TYPE_APPLICATION_OVERLAY, null /* options */);
+
+ verify(mockToken, never()).onConfigurationChanged(any(), anyInt());
+
+ // Turn on the display and verify if the client receive the callback
+ Display display = mContainer.getDisplayContent().getDisplay();
+ spyOn(display);
+ Mockito.doAnswer(invocation -> {
+ final DisplayInfo info = mContainer.getDisplayContent().getDisplayInfo();
+ info.state = STATE_ON;
+ ((DisplayInfo) invocation.getArgument(0)).copyFrom(info);
+ return null;
+ }).when(display).getDisplayInfo(any(DisplayInfo.class));
+
+ mContainer.getDisplayContent().onDisplayChanged();
+
+ assertThat(mockToken.mConfiguration).isEqualTo(config1);
+ assertThat(mockToken.mDisplayId).isEqualTo(mContainer.getDisplayContent().getDisplayId());
+ }
+
+ @Test
+ public void testReportConfigUpdateForSuspendedWindowProviderService() {
+ final TestWindowTokenClient clientToken = new TestWindowTokenClient();
+ final Bundle options = new Bundle();
+ options.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true);
+
+ mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;
+
+ final Configuration config1 = mContainer.getConfiguration();
+ final Rect bounds1 = new Rect(0, 0, 10, 10);
+ config1.windowConfiguration.setBounds(bounds1);
+ config1.densityDpi = 100;
+ mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+ mController.registerWindowContainerListener(clientToken, mContainer, -1,
+ TYPE_APPLICATION_OVERLAY, options);
+
+ assertThat(clientToken.mConfiguration).isEqualTo(config1);
+ assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId);
+ }
+
+ private static class TestWindowTokenClient extends IWindowToken.Stub {
private Configuration mConfiguration;
private int mDisplayId;
private boolean mRemoved;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 1eed79f1..75a87ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -554,7 +554,7 @@
final RunningTaskInfo info2 = task2.getTaskInfo();
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setAdjacentRoots(info1.token, info2.token);
+ wct.setAdjacentRoots(info1.token, info2.token, false /* moveTogether */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(task1.getAdjacentTaskFragment(), task2);
assertEquals(task2.getAdjacentTaskFragment(), task1);
@@ -564,8 +564,8 @@
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1);
- task1.setAdjacentTaskFragment(null);
- task2.setAdjacentTaskFragment(null);
+ task1.setAdjacentTaskFragment(null, false /* moveTogether */);
+ task2.setAdjacentTaskFragment(null, false /* moveTogether */);
wct = new WindowContainerTransaction();
wct.clearLaunchAdjacentFlagRoot(info1.token);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
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 88d8ba3..b997acf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -200,6 +200,13 @@
SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
mDefaultDisplay = mWm.mRoot.getDefaultDisplay();
+ // Update the display policy to make the screen fully turned on so animation is allowed
+ final DisplayPolicy displayPolicy = mDefaultDisplay.getDisplayPolicy();
+ displayPolicy.screenTurnedOn(null /* screenOnListener */);
+ displayPolicy.finishKeyguardDrawn();
+ displayPolicy.finishWindowsDrawn();
+ displayPolicy.finishScreenTurningOn();
+
mTransaction = mSystemServicesTestRule.mTransaction;
mMockSession = mock(Session.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index b3ba217..049966c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -23,7 +23,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -490,8 +489,8 @@
createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
final WindowState splitWindow2 =
createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
- splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2);
- splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1);
+ splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */);
+ splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */);
final Task aboveTask =
createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -501,8 +500,6 @@
mDisplayContent.assignChildLayers(mTransaction);
assertWindowHigher(splitWindow1, belowTaskWindow);
- assertWindowHigher(splitWindow1, belowTaskWindow);
- assertWindowHigher(splitWindow2, belowTaskWindow);
assertWindowHigher(splitWindow2, belowTaskWindow);
assertWindowHigher(mDockedDividerWindow, splitWindow1);
assertWindowHigher(mDockedDividerWindow, splitWindow2);
@@ -510,6 +507,39 @@
assertWindowHigher(pinnedWindow, aboveTaskWindow);
}
+
+ @Test
+ public void testDockedDividerPosition_noAboveTask() {
+ final Task pinnedTask =
+ createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ final WindowState pinnedWindow =
+ createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow");
+
+ final Task belowTask =
+ createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState belowTaskWindow =
+ createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow");
+
+ final Task splitScreenTask1 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final WindowState splitWindow1 =
+ createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1");
+ final Task splitScreenTask2 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final WindowState splitWindow2 =
+ createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
+ splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */);
+ splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */);
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowHigher(splitWindow1, belowTaskWindow);
+ assertWindowHigher(splitWindow2, belowTaskWindow);
+ assertWindowHigher(mDockedDividerWindow, splitWindow1);
+ assertWindowHigher(mDockedDividerWindow, splitWindow2);
+ assertWindowHigher(pinnedWindow, mDockedDividerWindow);
+ }
+
@Test
public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() {
// create RecentsAnimationController
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 85f16eb..661dcbb 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -175,10 +175,7 @@
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
// which need debouncing.
- private static final int DEVICE_STATE_UPDATE_DELAY = 3000;
-
- // Delay for debouncing USB disconnects on Type-C ports in host mode
- private static final int HOST_STATE_UPDATE_DELAY = 1000;
+ private static final int UPDATE_DELAY = 1000;
// Timeout for entering USB request mode.
// Request is cancelled if host does not configure device within 10 seconds.
@@ -639,7 +636,7 @@
msg.arg1 = connected;
msg.arg2 = configured;
// debounce disconnects to avoid problems bringing up USB tethering
- sendMessageDelayed(msg, (connected == 0) ? DEVICE_STATE_UPDATE_DELAY : 0);
+ sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
}
public void updateHostState(UsbPort port, UsbPortStatus status) {
@@ -654,7 +651,7 @@
removeMessages(MSG_UPDATE_PORT_STATE);
Message msg = obtainMessage(MSG_UPDATE_PORT_STATE, args);
// debounce rapid transitions of connect/disconnect on type-c ports
- sendMessageDelayed(msg, HOST_STATE_UPDATE_DELAY);
+ sendMessageDelayed(msg, UPDATE_DELAY);
}
private void setAdbEnabled(boolean enable) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index e19ea47..36bb375 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -23,11 +23,8 @@
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN;
import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS;
-import static com.android.server.voiceinteraction.SoundTriggerSessionPermissionsDecorator.enforcePermissionForPreflight;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.Context;
@@ -933,11 +930,12 @@
// TODO: Share this code with SoundTriggerMiddlewarePermission.
private void enforcePermissionsForDataDelivery() {
Binder.withCleanCallingIdentity(() -> {
- enforcePermissionForPreflight(mContext, mVoiceInteractorIdentity, RECORD_AUDIO);
- int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD);
- mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp,
- mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
- mVoiceInteractorIdentity.attributionTag, OP_MESSAGE);
+ // Hack to make sure we show the mic privacy-indicator since the Trusted Hotword
+ // requirement isn't being enforced for now. Normally, we would note the HOTWORD op here
+ // instead.
+ enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
+ RECORD_AUDIO, OP_MESSAGE);
+
enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
CAPTURE_AUDIO_HOTWORD, OP_MESSAGE);
});
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9c9e41b..b8a14b8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5794,6 +5794,7 @@
sDefaults.putInt(
KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
120000);
+ sDefaults.putAll(ImsServiceEntitlement.getDefaults());
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index c9a8947a..7b5f0b1 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -118,6 +118,8 @@
@Test
public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
+ doReturn(false).when(mDeps).isAirplaneModeOn(any());
+
mGatewayConnection
.getUnderlyingNetworkTrackerCallback()
.onSelectedUnderlyingNetworkChanged(null);
@@ -129,6 +131,19 @@
}
@Test
+ public void testNullNetworkAirplaneModeDisconnects() throws Exception {
+ doReturn(true).when(mDeps).isAirplaneModeOn(any());
+
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).kill();
+ }
+
+ @Test
public void testNewNetworkTriggersMigration() throws Exception {
mGatewayConnection
.getUnderlyingNetworkTrackerCallback()