mGetCurrentUserIdCache =
+ new IpcDataCache<>(1, IpcDataCache.MODULE_SYSTEM,
+ /* api= */ "getCurrentUserId", /* cacheName= */ "CurrentUserIdCache",
+ mGetCurrentUserIdQuery);
+
+ /**
+ * The current foreground user has changed - invalidate the cache. Currently only called from
+ * UserController when a user switch occurs.
+ * @hide
+ */
+ public static void invalidateGetCurrentUserIdCache() {
+ IpcDataCache.invalidateCache(
+ IpcDataCache.MODULE_SYSTEM, /* api= */ "getCurrentUserId");
+ }
+
/**
* Map of callbacks that have registered for {@link UidFrozenStateChanged} events.
* Will be called when a Uid has become frozen or unfrozen.
@@ -5244,11 +5283,7 @@ public class ActivityManager {
})
@android.ravenwood.annotation.RavenwoodReplace
public static int getCurrentUser() {
- try {
- return getService().getCurrentUserId();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mGetCurrentUserIdCache.query(null);
}
/** @hide */
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 21396a1a36e5bf1d1311090f58cfa952a8f13a43..f27dc322a2b73c6154e75b28ceca960fc8786174 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -79,7 +79,6 @@ import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Log;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.Pools;
@@ -3156,12 +3155,6 @@ public class AppOpsManager {
/** @hide */
public static final String KEY_HISTORICAL_OPS = "historical_ops";
- /** System properties for debug logging of noteOp call sites */
- private static final String DEBUG_LOGGING_ENABLE_PROP = "appops.logging_enabled";
- private static final String DEBUG_LOGGING_PACKAGES_PROP = "appops.logging_packages";
- private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
- private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
-
/**
* Retrieve the op switch that controls the given operation.
* @hide
@@ -7459,15 +7452,15 @@ public class AppOpsManager {
}
/**
- * Similar to {@link #onOpChanged(String, String, int)} but includes the device for which
- * the op mode has changed.
+ * Similar to {@link #onOpChanged(String, String)} but includes user and the device for
+ * which the op mode has changed.
*
* Implement this method if callbacks are required on all devices.
* If not implemented explicitly, the default implementation will notify for op changes
- * on the default device {@link VirtualDeviceManager#PERSISTENT_DEVICE_ID_DEFAULT} only.
+ * on the default device only.
*
- *
If implemented, {@link #onOpChanged(String, String, int)}
- * will not be called automatically.
+ *
If implemented, {@link #onOpChanged(String, String)} will not be called
+ * automatically.
*
* @param op The Op that changed.
* @param packageName Package of the app whose Op changed.
@@ -8066,14 +8059,6 @@ public class AppOpsManager {
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(int code, int uid, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Log.i(DEBUG_LOGGING_TAG,
- "setUidMode called for OP_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setUidMode(code, uid, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8094,15 +8079,6 @@ public class AppOpsManager {
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (appOp.equals(OPSTR_BLUETOOTH_CONNECT)) {
- Log.i(DEBUG_LOGGING_TAG,
- "setUidMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
-
mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8143,14 +8119,6 @@ public class AppOpsManager {
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setMode(int code, int uid, String packageName, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Log.i(DEBUG_LOGGING_TAG,
- "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setMode(code, uid, packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8173,14 +8141,6 @@ public class AppOpsManager {
public void setMode(@NonNull String op, int uid, @Nullable String packageName,
@Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (op.equals(OPSTR_BLUETOOTH_CONNECT)) {
- Log.i(DEBUG_LOGGING_TAG,
- "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setMode(strOpToOp(op), uid, packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 62b541261f34e8aa9620d4b3de9c28fc1009dc99..e0a937156906279464360682048eefbe1f34298b 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -49,7 +49,7 @@ public final class AutomaticZenRule implements Parcelable {
/**
* Rule is of an unknown type. This is the default value if not provided by the owning app,
- * and the value returned if the true type was added in an API level lower than the calling
+ * and the value returned if the true type was added in an API level higher than the calling
* app's targetSdk.
*/
@FlaggedApi(Flags.FLAG_MODES_API)
@@ -315,7 +315,8 @@ public final class AutomaticZenRule implements Parcelable {
}
/**
- * Gets the zen policy.
+ * Gets the {@link ZenPolicy} applied if {@link #getInterruptionFilter()} is
+ * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}.
*/
@Nullable
public ZenPolicy getZenPolicy() {
@@ -345,6 +346,17 @@ public final class AutomaticZenRule implements Parcelable {
/**
* Sets the interruption filter that is applied when this rule is active.
+ *
+ *
+ * - When {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}, the rule will use
+ * the {@link ZenPolicy} supplied to {@link #setZenPolicy} (or a default one).
+ *
- When {@link NotificationManager#INTERRUPTION_FILTER_ALARMS} or
+ * {@link NotificationManager#INTERRUPTION_FILTER_NONE}, the rule will use a fixed
+ * {@link ZenPolicy} matching the filter.
+ *
- When {@link NotificationManager#INTERRUPTION_FILTER_ALL}, the rule will not block
+ * notifications, but can still have {@link ZenDeviceEffects}.
+ *
+ *
* @param interruptionFilter The do not disturb mode to enter when this rule is active.
*/
public void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
@@ -374,7 +386,8 @@ public final class AutomaticZenRule implements Parcelable {
}
/**
- * Sets the zen policy.
+ * Sets the {@link ZenPolicy} applied if {@link #getInterruptionFilter()} is
+ * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}.
*
* When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule},
* a {@code null} value here means the previous policy is retained.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e99ba84276cd6ac582fb833d8fb3d40423dd2871..7a36fbb55dc42c1bdc3f97b5bd9614183fd424b5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2706,14 +2706,9 @@ public class Notification implements Parcelable
if (mAllowlistToken == null) {
mAllowlistToken = processAllowlistToken;
}
- if (Flags.secureAllowlistToken()) {
- // Propagate this token to all pending intents that are unmarshalled from the parcel,
- // or keep the one we're already propagating, if that's the case.
- if (!parcel.hasClassCookie(PendingIntent.class)) {
- parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
- }
- } else {
- // Propagate this token to all pending intents that are unmarshalled from the parcel.
+ // Propagate this token to all pending intents that are unmarshalled from the parcel,
+ // or keep the one we're already propagating, if that's the case.
+ if (!parcel.hasClassCookie(PendingIntent.class)) {
parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
}
@@ -3333,28 +3328,22 @@ public class Notification implements Parcelable
PendingIntent.addOnMarshaledListener(addedListener);
}
try {
- if (Flags.secureAllowlistToken()) {
- boolean mustClearCookie = false;
- if (!parcel.hasClassCookie(Notification.class)) {
- // This is the "root" notification, and not an "inner" notification (including
- // publicVersion or anything else that might be embedded in extras). So we want
- // to use its token for every inner notification (might be null).
- parcel.setClassCookie(Notification.class, mAllowlistToken);
- mustClearCookie = true;
- }
- try {
- // IMPORTANT: Add marshaling code in writeToParcelImpl as we
- // want to intercept all pending events written to the parcel.
- writeToParcelImpl(parcel, flags);
- } finally {
- if (mustClearCookie) {
- parcel.removeClassCookie(Notification.class, mAllowlistToken);
- }
- }
- } else {
+ boolean mustClearCookie = false;
+ if (!parcel.hasClassCookie(Notification.class)) {
+ // This is the "root" notification, and not an "inner" notification (including
+ // publicVersion or anything else that might be embedded in extras). So we want
+ // to use its token for every inner notification (might be null).
+ parcel.setClassCookie(Notification.class, mAllowlistToken);
+ mustClearCookie = true;
+ }
+ try {
// IMPORTANT: Add marshaling code in writeToParcelImpl as we
// want to intercept all pending events written to the parcel.
writeToParcelImpl(parcel, flags);
+ } finally {
+ if (mustClearCookie) {
+ parcel.removeClassCookie(Notification.class, mAllowlistToken);
+ }
}
synchronized (this) {
@@ -3371,13 +3360,9 @@ public class Notification implements Parcelable
private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
- if (Flags.secureAllowlistToken()) {
- // Always use the same token as the root notification (might be null).
- IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
- parcel.writeStrongBinder(rootNotificationToken);
- } else {
- parcel.writeStrongBinder(mAllowlistToken);
- }
+ // Always use the same token as the root notification (might be null).
+ IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
+ parcel.writeStrongBinder(rootNotificationToken);
parcel.writeLong(when);
parcel.writeLong(creationTime);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 789c99d8e0173736cb329ce8c79d48613577cc54..1b29b7a294df54cc19d9ba73b28d62ecb649a89a 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -168,7 +168,11 @@ public final class NotificationChannel implements Parcelable {
/**
* @hide
*/
- public static final int MAX_VIBRATION_LENGTH = 1000;
+ public static final int MAX_VIBRATION_LENGTH = 500;
+ /**
+ * @hide
+ */
+ public static final int MAX_SERIALIZED_VIBRATION_LENGTH = 32_768;
private static final String TAG_CHANNEL = "channel";
private static final String ATT_NAME = "name";
@@ -368,6 +372,9 @@ public final class NotificationChannel implements Parcelable {
if (Flags.notificationChannelVibrationEffectApi()) {
mVibrationEffect =
in.readInt() != 0 ? VibrationEffect.CREATOR.createFromParcel(in) : null;
+ if (Flags.notifChannelCropVibrationEffects() && mVibrationEffect != null) {
+ mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect);
+ }
}
mUserLockedFields = in.readInt();
mUserVisibleTaskShown = in.readByte() != 0;
@@ -582,6 +589,23 @@ public final class NotificationChannel implements Parcelable {
return input;
}
+ // Returns trimmed vibration effect or null if not trimmable.
+ private VibrationEffect getTrimmedVibrationEffect(VibrationEffect effect) {
+ if (effect == null) {
+ return null;
+ }
+ // trim if possible; check serialized length; reject if it is still too long
+ VibrationEffect result = effect;
+ VibrationEffect trimmed = effect.cropToLengthOrNull(MAX_VIBRATION_LENGTH);
+ if (trimmed != null) {
+ result = trimmed;
+ }
+ if (vibrationToString(result).length() > MAX_SERIALIZED_VIBRATION_LENGTH) {
+ return null;
+ }
+ return result;
+ }
+
/**
* @hide
*/
@@ -685,6 +709,11 @@ public final class NotificationChannel implements Parcelable {
public void setVibrationPattern(long[] vibrationPattern) {
this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
this.mVibrationPattern = vibrationPattern;
+ if (Flags.notifChannelCropVibrationEffects()) {
+ if (vibrationPattern != null && vibrationPattern.length > MAX_VIBRATION_LENGTH) {
+ this.mVibrationPattern = Arrays.copyOf(vibrationPattern, MAX_VIBRATION_LENGTH);
+ }
+ }
if (Flags.notificationChannelVibrationEffectApi()) {
try {
this.mVibrationEffect =
@@ -731,9 +760,21 @@ public final class NotificationChannel implements Parcelable {
public void setVibrationEffect(@Nullable VibrationEffect effect) {
this.mVibrationEnabled = effect != null;
this.mVibrationEffect = effect;
- this.mVibrationPattern =
- effect == null
- ? null : effect.computeCreateWaveformOffOnTimingsOrNull();
+ if (Flags.notifChannelCropVibrationEffects() && effect != null) {
+ // Try converting to a vibration pattern and trimming that array. If not convertible
+ // to a pattern directly, try trimming the vibration effect if possible and storing
+ // that version instead.
+ long[] pattern = effect.computeCreateWaveformOffOnTimingsOrNull();
+ if (pattern != null) {
+ setVibrationPattern(pattern);
+ } else {
+ this.mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect);
+ }
+ } else {
+ this.mVibrationPattern =
+ mVibrationEffect == null
+ ? null : mVibrationEffect.computeCreateWaveformOffOnTimingsOrNull();
+ }
}
/**
@@ -1172,7 +1213,9 @@ public final class NotificationChannel implements Parcelable {
if (vibrationEffect != null) {
// Restore the effect only if it is not null. This allows to avoid undoing a
// `setVibrationPattern` call above, if that was done with a non-null pattern
- // (e.g. back up from a version that did not support `setVibrationEffect`).
+ // (e.g. back up from a version that did not support `setVibrationEffect`), or
+ // when notif_channel_crop_vibration_effects is true, if there is an equivalent
+ // vibration pattern available.
setVibrationEffect(vibrationEffect);
}
}
@@ -1365,7 +1408,11 @@ public final class NotificationChannel implements Parcelable {
out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
}
if (getVibrationEffect() != null) {
- out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+ if (!Flags.notifChannelCropVibrationEffects() || getVibrationPattern() == null) {
+ // When notif_channel_crop_vibration_effects is on, only serialize the vibration
+ // effect if we do not already have an equivalent vibration pattern.
+ out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+ }
}
if (getUserLockedFields() != 0) {
out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 14195c473c6d7e202f818fc4a2f327670997f4bf..63e039143917985d3f19aaa2e1929c642b9fb11b 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -1326,6 +1326,7 @@ public class StatusBarManager {
private boolean mClock;
private boolean mNotificationIcons;
private boolean mRotationSuggestion;
+ private boolean mQuickSettings;
/** @hide */
public DisableInfo(int flags1, int flags2) {
@@ -1338,6 +1339,7 @@ public class StatusBarManager {
mClock = (flags1 & DISABLE_CLOCK) != 0;
mNotificationIcons = (flags1 & DISABLE_NOTIFICATION_ICONS) != 0;
mRotationSuggestion = (flags2 & DISABLE2_ROTATE_SUGGESTIONS) != 0;
+ mQuickSettings = (flags2 & DISABLE2_QUICK_SETTINGS) != 0;
}
/** @hide */
@@ -1470,6 +1472,20 @@ public class StatusBarManager {
return mRotationSuggestion;
}
+ /**
+ * @hide
+ */
+ public void setQuickSettingsDisabled(boolean disabled) {
+ mQuickSettings = disabled;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isQuickSettingsDisabled() {
+ return mQuickSettings;
+ }
+
/**
* @return {@code true} if no components are disabled (default state)
* @hide
@@ -1478,7 +1494,7 @@ public class StatusBarManager {
public boolean areAllComponentsEnabled() {
return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
&& !mSearch && !mSystemIcons && !mClock && !mNotificationIcons
- && !mRotationSuggestion;
+ && !mRotationSuggestion && !mQuickSettings;
}
/** @hide */
@@ -1492,6 +1508,7 @@ public class StatusBarManager {
mClock = false;
mNotificationIcons = false;
mRotationSuggestion = false;
+ mQuickSettings = false;
}
/**
@@ -1502,7 +1519,7 @@ public class StatusBarManager {
public boolean areAllComponentsDisabled() {
return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
&& mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons
- && mRotationSuggestion;
+ && mRotationSuggestion && mQuickSettings;
}
/** @hide */
@@ -1516,6 +1533,7 @@ public class StatusBarManager {
mClock = true;
mNotificationIcons = true;
mRotationSuggestion = true;
+ mQuickSettings = true;
}
@NonNull
@@ -1533,6 +1551,7 @@ public class StatusBarManager {
sb.append(" mClock=").append(mClock ? "disabled" : "enabled");
sb.append(" mNotificationIcons=").append(mNotificationIcons ? "disabled" : "enabled");
sb.append(" mRotationSuggestion=").append(mRotationSuggestion ? "disabled" : "enabled");
+ sb.append(" mQuickSettings=").append(mQuickSettings ? "disabled" : "enabled");
return sb.toString();
@@ -1557,6 +1576,7 @@ public class StatusBarManager {
if (mClock) disable1 |= DISABLE_CLOCK;
if (mNotificationIcons) disable1 |= DISABLE_NOTIFICATION_ICONS;
if (mRotationSuggestion) disable2 |= DISABLE2_ROTATE_SUGGESTIONS;
+ if (mQuickSettings) disable2 |= DISABLE2_QUICK_SETTINGS;
return new Pair(disable1, disable2);
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index cb38cf297cf6ec4d1714fb05f32c1f9e12b91862..e44e7768724efd57306159b074fbb0e503bde80b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -31,6 +31,7 @@ import android.app.admin.IDevicePolicyManager;
import android.app.ambientcontext.AmbientContextManager;
import android.app.ambientcontext.IAmbientContextManager;
import android.app.appfunctions.AppFunctionManager;
+import android.app.appfunctions.AppFunctionManagerConfiguration;
import android.app.appfunctions.IAppFunctionManager;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
@@ -48,6 +49,8 @@ import android.app.sdksandbox.SdkSandboxManagerFrameworkInitializer;
import android.app.search.SearchUiManager;
import android.app.slice.SliceManager;
import android.app.smartspace.SmartspaceManager;
+import android.app.supervision.ISupervisionManager;
+import android.app.supervision.SupervisionManager;
import android.app.time.TimeManager;
import android.app.timedetector.TimeDetector;
import android.app.timedetector.TimeDetectorImpl;
@@ -935,8 +938,10 @@ public final class SystemServiceRegistry {
@Override
public AppFunctionManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
+ if (!AppFunctionManagerConfiguration.isSupported(ctx)) {
+ return null;
+ }
IAppFunctionManager service;
- //TODO(b/357551503): If the feature not present avoid look up every time
service = IAppFunctionManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.APP_FUNCTION_SERVICE));
return new AppFunctionManager(service, ctx.getOuterContext());
@@ -1703,6 +1708,21 @@ public final class SystemServiceRegistry {
return new E2eeContactKeysManager(ctx);
}});
+ registerService(Context.SUPERVISION_SERVICE, SupervisionManager.class,
+ new CachedServiceFetcher<>() {
+ @Override
+ public SupervisionManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ if (!android.app.supervision.flags.Flags.supervisionApi()) {
+ throw new ServiceNotFoundException(
+ "SupervisionManager is not supported");
+ }
+ IBinder iBinder = ServiceManager.getServiceOrThrow(
+ Context.SUPERVISION_SERVICE);
+ ISupervisionManager service = ISupervisionManager.Stub.asInterface(iBinder);
+ return new SupervisionManager(ctx, service);
+ }
+ });
// DO NOT do a flag check like this unless the flag is read-only.
// (because this code is executed during preload in zygote.)
// If the flag is mutable, the check should be inside CachedServiceFetcher.
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 32e6e80cc3e9257d85a91663c2283bb618716b10..4d61f418af10eee24a7a341cadc20e087d4ef046 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -104,3 +104,24 @@ flag {
}
}
+flag {
+ namespace: "backstage_power"
+ name: "use_app_info_not_launched"
+ description: "Use the notLaunched state from ApplicationInfo instead of current value"
+ is_fixed_read_only: true
+ bug: "362516211"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "backstage_power"
+ name: "cache_get_current_user_id"
+ description: "Add caching for getCurrentUserId"
+ is_fixed_read_only: true
+ bug: "361853873"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index 02e492bb06aa169944479d5c58c60f5365e2a8a5..515c1c66b2a3df4d0f7a4d846addf5ad95f1ace6 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -24,7 +24,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -54,9 +53,7 @@ public final class AccountTypePolicyKey extends PolicyKey {
@TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
- }
+ PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
mAccountType = Objects.requireNonNull((accountType));
}
diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java
index c993671f4fc176b669981307eb43612ed63e62f8..00e67e64502afd1396a844cd1e2de2f7ce049cd8 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -18,7 +18,6 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -31,9 +30,7 @@ public final class BundlePolicyValue extends PolicyValue {
public BundlePolicyValue(Bundle value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
- }
+ PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
}
private BundlePolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java
index a7a2f7d27e0d16c12ad65fd2d757830d0af88ce2..f092b7bb553821f12ac36ebd1ad78aac9333af39 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -18,7 +18,6 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.Parcel;
@@ -31,9 +30,7 @@ public final class ComponentNamePolicyValue extends PolicyValue {
public ComponentNamePolicyValue(@NonNull ComponentName value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxComponentNameLength(value);
- }
+ PolicySizeVerifier.enforceMaxComponentNameLength(value);
}
private ComponentNamePolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 46c9e781bed145e73dc04545cedb11d090effde0..cb2b8adae0f9587a17f4638abd6ffe944f1facbc 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -16,12 +16,8 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
-
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.app.admin.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -195,7 +191,6 @@ public final class DeviceAdminInfo implements Parcelable {
* DPCs should set the value of attribute "headless-device-owner-mode" inside the
* "headless-system-user" tag as "single_user".
*/
- @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2;
/**
@@ -392,11 +387,8 @@ public final class DeviceAdminInfo implements Parcelable {
}
mSupportsTransferOwnership = true;
} else if (tagName.equals("headless-system-user")) {
- String deviceOwnerModeStringValue = null;
- if (Flags.headlessSingleUserCompatibilityFix()) {
- deviceOwnerModeStringValue = parser.getAttributeValue(
- null, "headless-device-owner-mode");
- }
+ String deviceOwnerModeStringValue = parser.getAttributeValue(
+ null, "headless-device-owner-mode");
if (deviceOwnerModeStringValue == null) {
deviceOwnerModeStringValue =
parser.getAttributeValue(null, "device-owner-mode");
@@ -409,13 +401,8 @@ public final class DeviceAdminInfo implements Parcelable {
} else if ("single_user".equalsIgnoreCase(deviceOwnerModeStringValue)) {
mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
} else {
- if (Flags.headlessSingleUserCompatibilityFix()) {
- Log.e(TAG, "Unknown headless-system-user mode: "
- + deviceOwnerModeStringValue);
- } else {
- throw new XmlPullParserException(
- "headless-system-user mode must be valid");
- }
+ Log.e(TAG, "Unknown headless-system-user mode: "
+ + deviceOwnerModeStringValue);
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 156512a9029565ada8ce15856c834fd1d36a37c1..c0e435c04d3cb72d8fe746c024074bf0ab70314f 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -16,8 +16,6 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED;
-
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -185,13 +183,11 @@ public final class DevicePolicyIdentifiers {
/**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
*/
- @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
/**
* String identifier for {@link DevicePolicyManager#setRequiredPasswordComplexity}.
*/
- @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 5088ea6b603c15f5919f159675f2ef24c210aff4..daa15f05d942e6682edca47b001bd2bbd4e44c8e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -54,15 +54,9 @@ import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
-import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -2988,7 +2982,6 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17;
/**
@@ -4237,7 +4230,6 @@ public class DevicePolicyManager {
*
* @return whether MTE is currently enabled on the device.
*/
- @FlaggedApi(FLAG_IS_MTE_POLICY_ENFORCED)
public static boolean isMtePolicyEnforced() {
return Zygote.nativeSupportsMemoryTagging();
}
@@ -8669,6 +8661,7 @@ public class DevicePolicyManager {
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_CAMERA, conditional = true)
+ @SupportsCoexistence
public void setCameraDisabled(@Nullable ComponentName admin, boolean disabled) {
if (mService != null) {
try {
@@ -10254,6 +10247,7 @@ public class DevicePolicyManager {
* permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_TASK}.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK_TASK, conditional = true)
+ @SupportsCoexistence
public void clearPackagePersistentPreferredActivities(@Nullable ComponentName admin,
String packageName) {
throwIfParentInstance("clearPackagePersistentPreferredActivities");
@@ -10481,10 +10475,6 @@ public class DevicePolicyManager {
@WorkerThread
public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
Bundle settings) {
- if (!Flags.dmrhSetAppRestrictions()) {
- throwIfParentInstance("setApplicationRestrictions");
- }
-
if (mService != null) {
try {
mService.setApplicationRestrictions(admin, mContext.getPackageName(), packageName,
@@ -11889,9 +11879,6 @@ public class DevicePolicyManager {
@WorkerThread
public @NonNull Bundle getApplicationRestrictions(
@Nullable ComponentName admin, String packageName) {
- if (!Flags.dmrhSetAppRestrictions()) {
- throwIfParentInstance("getApplicationRestrictions");
- }
if (mService != null) {
try {
@@ -11952,6 +11939,7 @@ public class DevicePolicyManager {
* @throws SecurityException if {@code admin} is not a device or profile owner and if the caller
* has not been granted the permission to set the given user restriction.
*/
+ @SupportsCoexistence
public void addUserRestriction(@NonNull ComponentName admin,
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
@@ -12033,6 +12021,7 @@ public class DevicePolicyManager {
* @throws IllegalStateException if caller is not targeting Android
* {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above.
*/
+ @SupportsCoexistence
public void addUserRestrictionGlobally(@NonNull @UserManager.UserRestrictionKey String key) {
throwIfParentInstance("addUserRestrictionGlobally");
if (mService != null) {
@@ -12088,6 +12077,7 @@ public class DevicePolicyManager {
* @throws SecurityException if {@code admin} is not a device or profile owner and if the
* caller has not been granted the permission to set the given user restriction.
*/
+ @SupportsCoexistence
public void clearUserRestriction(@NonNull ComponentName admin,
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
@@ -12324,6 +12314,7 @@ public class DevicePolicyManager {
* @see #DELEGATION_PACKAGE_ACCESS
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_PACKAGE_STATE, conditional = true)
+ @SupportsCoexistence
public boolean setApplicationHidden(@Nullable ComponentName admin, String packageName,
boolean hidden) {
if (mService != null) {
@@ -12504,6 +12495,7 @@ public class DevicePolicyManager {
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, conditional = true)
+ @SupportsCoexistence
public void setAccountManagementDisabled(@Nullable ComponentName admin, String accountType,
boolean disabled) {
if (mService != null) {
@@ -12587,10 +12579,24 @@ public class DevicePolicyManager {
**/
@SystemApi
public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) {
+ setSecondaryLockscreenEnabled(admin, enabled, null);
+ }
+
+ /**
+ * Called by the system supervision app to set whether a secondary lockscreen needs to be shown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
+ * caller is not a device admin.
+ * @param enabled Whether or not the lockscreen needs to be shown.
+ * @param options A {@link PersistableBundle} to supply options to the lock screen.
+ * @hide
+ */
+ public void setSecondaryLockscreenEnabled(@Nullable ComponentName admin, boolean enabled,
+ @Nullable PersistableBundle options) {
throwIfParentInstance("setSecondaryLockscreenEnabled");
if (mService != null) {
try {
- mService.setSecondaryLockscreenEnabled(admin, enabled);
+ mService.setSecondaryLockscreenEnabled(admin, enabled, options);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -14236,21 +14242,11 @@ public class DevicePolicyManager {
*/
public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
throwIfParentInstance("getParentProfileInstance");
- try {
- if (Flags.dmrhSetAppRestrictions()) {
- UserManager um = mContext.getSystemService(UserManager.class);
- if (!um.isManagedProfile()) {
- throw new SecurityException("The current user does not have a parent profile.");
- }
- } else {
- if (!mService.isManagedProfile(admin)) {
- throw new SecurityException("The current user does not have a parent profile.");
- }
- }
- return new DevicePolicyManager(mContext, mService, true);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ UserManager um = mContext.getSystemService(UserManager.class);
+ if (!um.isManagedProfile()) {
+ throw new SecurityException("The current user does not have a parent profile.");
}
+ return new DevicePolicyManager(mContext, mService, true);
}
/**
@@ -14298,6 +14294,7 @@ public class DevicePolicyManager {
* @see #retrieveSecurityLogs
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_SECURITY_LOGGING, conditional = true)
+ @SupportsCoexistence
public void setSecurityLoggingEnabled(@Nullable ComponentName admin, boolean enabled) {
throwIfParentInstance("setSecurityLoggingEnabled");
try {
@@ -17203,6 +17200,7 @@ public class DevicePolicyManager {
* if USB data signaling fails to be enabled/disabled.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, conditional = true)
+ @SupportsCoexistence
public void setUsbDataSignalingEnabled(boolean enabled) {
throwIfParentInstance("setUsbDataSignalingEnabled");
if (mService != null) {
@@ -17744,7 +17742,6 @@ public class DevicePolicyManager {
* @throws SecurityException if the caller is not authorized to call this method.
* @return ids of all managed subscriptions currently downloaded by an admin on the device.
*/
- @FlaggedApi(FLAG_ESIM_MANAGEMENT_ENABLED)
@RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS)
@NonNull
public Set getSubscriptionIds() {
@@ -17769,7 +17766,6 @@ public class DevicePolicyManager {
*/
@SystemApi
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED)
public void setMaxPolicyStorageLimit(int storageLimit) {
if (mService != null) {
try {
@@ -17789,7 +17785,6 @@ public class DevicePolicyManager {
*/
@SystemApi
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED)
public int getMaxPolicyStorageLimit() {
if (mService != null) {
try {
@@ -17813,7 +17808,6 @@ public class DevicePolicyManager {
*/
@TestApi
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
public void forceSetMaxPolicyStorageLimit(int storageLimit) {
if (mService != null) {
try {
@@ -17831,7 +17825,6 @@ public class DevicePolicyManager {
*/
@TestApi
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
public int getPolicySizeForAdmin(@NonNull EnforcingAdmin admin) {
if (mService != null) {
try {
@@ -17850,13 +17843,9 @@ public class DevicePolicyManager {
* @hide
*/
@TestApi
- @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED)
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
@DeviceAdminInfo.HeadlessDeviceOwnerMode
public int getHeadlessDeviceOwnerMode() {
- if (!Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
- return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
- }
if (mService != null) {
try {
return mService.getHeadlessDeviceOwnerMode(mContext.getPackageName());
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index f70a53f61671cbc62fdc438823c3f6d02e73e300..5f9bb9c22893ecd9fd3a8d045471edb8fd3c3ba7 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -16,9 +16,6 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
-
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -64,7 +61,6 @@ public final class EnforcingAdmin implements Parcelable {
*
* @hide
*/
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
@TestApi
public EnforcingAdmin(
@NonNull String packageName, @NonNull Authority authority,
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d4e5c9960c2a3ca65cbf64b16476321e76928abf..a4e2b8f62a23e54bcdb8833ce46a56259942e79d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -303,7 +303,7 @@ interface IDevicePolicyManager {
String[] getAccountTypesWithManagementDisabled(String callerPackageName);
String[] getAccountTypesWithManagementDisabledAsUser(int userId, String callerPackageName, in boolean parent);
- void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled);
+ void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled, in PersistableBundle options);
boolean isSecondaryLockscreenEnabled(in UserHandle userHandle);
void setPreferentialNetworkServiceConfigs(
diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java
index 68b4ad84d81a8a75da339418bc68d64f4f114eff..ab32d46a05ad090bb843a5c6c2bc64831b30b970 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -19,7 +19,6 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.app.admin.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -135,10 +134,8 @@ public final class LockTaskPolicy extends PolicyValue {
}
private void setPackagesInternal(Set packages) {
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- for (String p : packages) {
- PolicySizeVerifier.enforceMaxPackageNameLength(p);
- }
+ for (String p : packages) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(p);
}
mPackages = new HashSet<>(packages);
}
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 1a04f6c908bc3bc55a78cf8b20053d24fada4465..226c576d9bc339757dcb4286814336d576165878 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -25,7 +25,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -59,10 +58,8 @@ public final class PackagePermissionPolicyKey extends PolicyKey {
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
- }
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
+ PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
mPackageName = Objects.requireNonNull((packageName));
mPermissionName = Objects.requireNonNull((permissionName));
}
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 9e31a23aec91c4e6a369a8723e467fcd60a61160..8fa21dbb0a2e279987c8aad8113d8e0ee28468e0 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -24,7 +24,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -55,9 +54,7 @@ public final class PackagePolicyKey extends PolicyKey {
@TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- }
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
mPackageName = Objects.requireNonNull((packageName));
}
diff --git a/core/java/android/app/admin/PackageSetPolicyValue.java b/core/java/android/app/admin/PackageSetPolicyValue.java
index 8b253a23a2994d237d42756a282d3fdf0b95b19d..24c50b0994d7c649edfede08e2dc2911a210a531 100644
--- a/core/java/android/app/admin/PackageSetPolicyValue.java
+++ b/core/java/android/app/admin/PackageSetPolicyValue.java
@@ -18,7 +18,6 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.HashSet;
@@ -32,10 +31,8 @@ public final class PackageSetPolicyValue extends PolicyValue> {
public PackageSetPolicyValue(@NonNull Set value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- for (String packageName : value) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- }
+ for (String packageName : value) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
}
}
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 477f2e007b332ad153c9d221270a22f0918bdf7c..beb93fd079d9424ec47caf58897c7f894aa0a516 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -16,10 +16,7 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED;
-
import android.Manifest;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -611,7 +608,6 @@ public class SecurityLog {
* [2] backup service state ({@code Integer}, 1 for enabled, 0 for disabled)
* @see DevicePolicyManager#setBackupServiceEnabled(ComponentName, boolean)
*/
- @FlaggedApi(FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED)
public static final int TAG_BACKUP_SERVICE_TOGGLED =
SecurityLogTags.SECURITY_BACKUP_SERVICE_TOGGLED;
/**
diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java
index 6efe9ad0dbedebf9b9a5466c23400e2c68af1941..bb07c23163ea8c425dd0e7a4b02109e25a997db2 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -18,7 +18,6 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.Objects;
@@ -30,9 +29,7 @@ public final class StringPolicyValue extends PolicyValue {
public StringPolicyValue(@NonNull String value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
- }
+ PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
}
private StringPolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index 9054287cb7a007c5abe4ca18a7f9f205ce0b5dec..16cfba4414d5dcbdb676811288e1e6678b6e4575 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -21,7 +21,6 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -45,9 +44,7 @@ public final class UserRestrictionPolicyKey extends PolicyKey {
@TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
- }
+ PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
mRestriction = Objects.requireNonNull(restriction);
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 56f47922b078548bd11cafa05b5685af29845ae5..8e08a95dad704b0c2b5d07ec76b9b383e68ef1d8 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -4,6 +4,7 @@
package: "android.app.admin.flags"
container: "system"
+# Fully rolled out and must not be used.
flag {
name: "policy_engine_migration_v2_enabled"
is_exported: true
@@ -12,6 +13,7 @@ flag {
bug: "289520697"
}
+# Fully rolled out and must not be used.
flag {
name: "device_policy_size_tracking_enabled"
is_exported: true
@@ -20,23 +22,6 @@ flag {
bug: "281543351"
}
-flag {
- name: "device_policy_size_tracking_internal_enabled"
- namespace: "enterprise"
- description: "Add feature to track the total policy size and have a max threshold - internal changes"
- bug: "281543351"
-}
-
-flag {
- name: "device_policy_size_tracking_internal_bug_fix_enabled"
- namespace: "enterprise"
- description: "Bug fix for tracking the total policy size and have a max threshold"
- bug: "281543351"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
flag {
name: "onboarding_bugreport_v2_enabled"
is_exported: true
@@ -53,13 +38,7 @@ flag {
is_fixed_read_only: true
}
-flag {
- name: "dedicated_device_control_enabled"
- namespace: "enterprise"
- description: "Allow the device management role holder to control which platform features are available on dedicated devices."
- bug: "281964214"
-}
-
+# Fully rolled out and must not be used.
flag {
name: "dedicated_device_control_api_enabled"
is_exported: true
@@ -76,13 +55,6 @@ flag {
bug: "289520697"
}
-flag {
- name: "permission_migration_for_zero_trust_impl_enabled"
- namespace: "enterprise"
- description: "(Implementation) Migrate existing APIs to permission based, and enable DMRH to call them to collect Zero Trust signals."
- bug: "289520697"
-}
-
flag {
name: "device_theft_api_enabled"
is_exported: true
@@ -99,36 +71,76 @@ flag {
}
flag {
- name: "coexistence_migration_for_non_emm_management_enabled"
+ name: "coexistence_migration_for_supervision_enabled"
+ is_exported: true
namespace: "enterprise"
- description: "Migrate existing APIs to be coexistable, and enable DMRH to call them to support non-EMM device management."
- bug: "289520697"
+ description: "Migrate existing APIs that are used by supervision (Kids Module) to be coexistable."
+ bug: "356894721"
}
-# Fully rolled out and must not be used.
flag {
- name: "security_log_v2_enabled"
- is_exported: true
- namespace: "enterprise"
- description: "Improve access to security logging in the context of Zero Trust."
- bug: "295324350"
+ name: "reset_password_with_token_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for resetPasswordWithToken and setResetPasswordToken."
+ bug: "359187209"
}
flag {
- name: "hsum_unlock_notification_fix"
- namespace: "enterprise"
- description: "Using the right userId when starting the work profile unlock flow "
- bug: "327350831"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ name: "set_keyguard_disabled_features_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setKeyguardDisabledFeatures."
+ bug: "359186276"
+}
+
+flag {
+ name: "set_application_restrictions_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setApplicationRestrictions."
+ bug: "359188153"
+}
+
+flag {
+ name: "set_auto_time_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setAutoTimeEnabled."
+ bug: "359188869"
+}
+
+flag {
+ name: "set_backup_service_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setBackupServiceEnabled."
+ bug: "359188483"
}
flag {
- name: "dumpsys_policy_engine_migration_enabled"
+ name: "set_auto_time_zone_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setAutoTimeZoneEnabled."
+ bug: "364338300"
+}
+
+flag {
+ name: "set_permission_grant_state_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setPermissionGrantState."
+ bug: "364338410"
+}
+
+# Fully rolled out and must not be used.
+flag {
+ name: "security_log_v2_enabled"
+ is_exported: true
namespace: "enterprise"
- description: "Update DumpSys to include information about migrated APIs in DPE"
- bug: "304999634"
+ description: "Improve access to security logging in the context of Zero Trust."
+ bug: "295324350"
}
flag {
@@ -146,6 +158,7 @@ flag {
bug: "293441361"
}
+# Fully rolled out and must not be used.
flag {
name: "assist_content_user_restriction_enabled"
is_exported: true
@@ -164,6 +177,7 @@ flag {
}
}
+# Fully rolled out and must not be used.
flag {
name: "backup_service_security_log_event_enabled"
is_exported: true
@@ -172,6 +186,7 @@ flag {
bug: "304999634"
}
+# Fully rolled out and must not be used.
flag {
name: "esim_management_enabled"
is_exported: true
@@ -180,6 +195,7 @@ flag {
bug: "295301164"
}
+# Fully rolled out and must not be used.
flag {
name: "headless_device_owner_single_user_enabled"
is_exported: true
@@ -188,6 +204,7 @@ flag {
bug: "289515470"
}
+# Fully rolled out and must not be used.
flag {
name: "is_mte_policy_enforced"
is_exported: true
@@ -196,36 +213,6 @@ flag {
bug: "322777918"
}
-flag {
- name: "copy_account_with_retry_enabled"
- namespace: "enterprise"
- description: "Retry copy and remove account from personal to work profile in case of failure"
- bug: "329424312"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "power_exemption_bg_usage_fix"
- namespace: "enterprise"
- description: "Ensure aps with EXEMPT_FROM_POWER_RESTRICTIONS can execute in the background"
- bug: "333379020"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "disallow_user_control_bg_usage_fix"
- namespace: "enterprise"
- description: "Make DPM.setUserControlDisabledPackages() ensure background usage is allowed"
- bug: "326031059"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
flag {
name: "disallow_user_control_stopped_state_fix"
namespace: "enterprise"
@@ -243,43 +230,6 @@ flag {
bug: "295301164"
}
-flag {
- name: "headless_device_owner_provisioning_fix_enabled"
- namespace: "enterprise"
- description: "Fix provisioning for single-user headless DO"
- bug: "289515470"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "dmrh_set_app_restrictions"
- namespace: "enterprise"
- description: "Allow DMRH to set application restrictions (both on the profile and the parent)"
- bug: "328758346"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "allow_screen_brightness_control_on_cope"
- namespace: "enterprise"
- description: "Allow COPE admin to control screen brightness and timeout."
- bug: "323894620"
-}
-
-flag {
- name: "always_persist_do"
- namespace: "enterprise"
- description: "Always write device_owners2.xml so that migration flags aren't lost"
- bug: "335232744"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
flag {
name: "is_recursive_required_app_merging_enabled"
namespace: "enterprise"
@@ -287,26 +237,6 @@ flag {
bug: "319084618"
}
-flag {
- name: "headless_device_owner_delegate_security_logging_bug_fix"
- namespace: "enterprise"
- description: "Fix delegate security logging for single user headless DO."
- bug: "289515470"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "headless_single_user_bad_device_admin_state_fix"
- namespace: "enterprise"
- description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
- bug: "332477138"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
flag {
name: "onboarding_bugreport_storage_bug_fix"
namespace: "enterprise"
@@ -317,16 +247,6 @@ flag {
}
}
-flag {
- name: "delete_private_space_under_restriction"
- namespace: "enterprise"
- description: "Delete private space if user restriction is set"
- bug: "328758346"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
flag {
name: "unmanaged_mode_migration"
namespace: "enterprise"
@@ -337,16 +257,6 @@ flag {
}
}
-flag {
- name: "headless_single_user_fixes"
- namespace: "enterprise"
- description: "Various fixes for headless single user mode"
- bug: "289515470"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
flag {
name: "backup_connected_apps_settings"
namespace: "enterprise"
@@ -354,16 +264,6 @@ flag {
bug: "175067666"
}
-flag {
- name: "headless_single_user_compatibility_fix"
- namespace: "enterprise"
- description: "Fix for compatibility issue introduced from using single_user mode on pre-Android V builds"
- bug: "338050276"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
flag {
name: "headless_single_min_target_sdk"
namespace: "enterprise"
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index b6240a79dda8f824096ddbda1c78d51bafc7c844..4682f3d30e1eeb83aae9e4e63648ff5035380e9c 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -49,9 +49,8 @@ public final class AppFunctionManager {
/**
* Creates an instance.
*
- * @param mService An interface to the backing service.
+ * @param service An interface to the backing service.
* @param context A {@link Context}.
- *
* @hide
*/
public AppFunctionManager(IAppFunctionManager service, Context context) {
@@ -61,42 +60,42 @@ public final class AppFunctionManager {
/**
* Executes the app function.
- *
- * Note: Applications can execute functions they define. To execute functions defined in
- * another component, apps would need to have
- * {@code android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or
- * {@code android.permission.EXECUTE_APP_FUNCTIONS}.
*
- * @param request the request to execute the app function
+ *
Note: Applications can execute functions they define. To execute functions defined in
+ * another component, apps would need to have {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS}.
+ *
+ * @param request the request to execute the app function
* @param executor the executor to run the callback
* @param callback the callback to receive the function execution result. if the calling app
- * does not own the app function or does not have {@code
- * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
- * android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain
- * {@code ExecuteAppFunctionResponse.RESULT_DENIED}.
+ * does not own the app function or does not have {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
+ * ExecuteAppFunctionResponse.RESULT_DENIED}.
*/
// TODO(b/360864791): Document that apps can opt-out from being executed by callers with
// EXECUTE_APP_FUNCTIONS and how a caller knows whether a function is opted out.
// TODO(b/357551503): Update documentation when get / set APIs are implemented that this will
// also return RESULT_DENIED if the app function is disabled.
@RequiresPermission(
- anyOf = {Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
- Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional = true)
+ anyOf = {
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+ Manifest.permission.EXECUTE_APP_FUNCTIONS
+ },
+ conditional = true)
@UserHandleAware
public void executeAppFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull @CallbackExecutor Executor executor,
- @NonNull Consumer callback
- ) {
+ @NonNull Consumer callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
ExecuteAppFunctionAidlRequest aidlRequest =
new ExecuteAppFunctionAidlRequest(
- request,
- mContext.getUser(),
- mContext.getPackageName());
+ request, mContext.getUser(), mContext.getPackageName());
try {
mService.executeAppFunction(
aidlRequest,
@@ -108,8 +107,11 @@ public final class AppFunctionManager {
} catch (RuntimeException e) {
// Ideally shouldn't happen since errors are wrapped into the
// response, but we catch it here for additional safety.
- callback.accept(new ExecuteAppFunctionResponse.Builder(
- getResultCode(e), e.getMessage()).build());
+ callback.accept(
+ ExecuteAppFunctionResponse.newFailure(
+ getResultCode(e),
+ e.getMessage(),
+ /* extras= */ null));
}
}
});
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa77e793fbe9d08e8895f780fd2fe6129790caea
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+/**
+ * Represents the system configuration of support for the {@code AppFunctionManager} and associated
+ * systems.
+ *
+ * @hide
+ */
+public class AppFunctionManagerConfiguration {
+ private final Context mContext;
+
+ /**
+ * Constructs a new instance of {@code AppFunctionManagerConfiguration}.
+ *
+ * @param context context
+ */
+ public AppFunctionManagerConfiguration(@NonNull final Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Indicates whether the current target is intended to support {@code AppFunctionManager}.
+ *
+ * @return {@code true} if supported; otherwise {@code false}
+ */
+ public boolean isSupported() {
+ return enableAppFunctionManager() && !isWatch();
+ }
+
+ /**
+ * Indicates whether the current target is intended to support {@code AppFunctionManager}.
+ *
+ * @param context context
+ * @return {@code true} if supported; otherwise {@code false}
+ */
+ public static boolean isSupported(@NonNull final Context context) {
+ return new AppFunctionManagerConfiguration(context).isSupported();
+ }
+
+ private boolean isWatch() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6f45e4c9f6af5c7e52d94a645cc66c76a7ba782
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appfunctions;
+
+import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID;
+import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_ENABLED;
+import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_INDEXER_PACKAGE;
+import static android.app.appfunctions.AppFunctionStaticMetadataHelper.STATIC_PROPERTY_ENABLED_BY_DEFAULT;
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.GlobalSearchSession;
+import android.app.appsearch.JoinSpec;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+import android.app.appsearch.SearchSpec;
+import android.os.OutcomeReceiver;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class containing utilities for {@link AppFunctionManager}.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionManagerHelper {
+
+ /**
+ * Returns (through a callback) a boolean indicating whether the app function is enabled.
+ *
+ * This method can only check app functions that are owned by the caller owned by packages
+ * visible to the caller.
+ *
+ *
If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
+ *
+ *
+ * - {@link IllegalArgumentException}, if the function is not found
+ *
- {@link SecurityException}, if the caller does not have permission to query the target
+ * package
+ *
+ *
+ * @param functionIdentifier the identifier of the app function to check (unique within the
+ * target package) and in most cases, these are automatically generated by the AppFunctions
+ * SDK
+ * @param targetPackage the package name of the app function's owner
+ * @param appSearchExecutor the executor to run the metadata search mechanism through AppSearch
+ * @param callbackExecutor the executor to run the callback
+ * @param callback the callback to receive the function enabled check result
+ * @hide
+ */
+ public static void isAppFunctionEnabled(
+ @NonNull String functionIdentifier,
+ @NonNull String targetPackage,
+ @NonNull AppSearchManager appSearchManager,
+ @NonNull Executor appSearchExecutor,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver callback) {
+ Objects.requireNonNull(functionIdentifier);
+ Objects.requireNonNull(targetPackage);
+ Objects.requireNonNull(appSearchManager);
+ Objects.requireNonNull(appSearchExecutor);
+ Objects.requireNonNull(callbackExecutor);
+ Objects.requireNonNull(callback);
+
+ appSearchManager.createGlobalSearchSession(
+ appSearchExecutor,
+ (searchSessionResult) -> {
+ if (!searchSessionResult.isSuccess()) {
+ callbackExecutor.execute(
+ () ->
+ callback.onError(
+ failedResultToException(searchSessionResult)));
+ return;
+ }
+ try (GlobalSearchSession searchSession = searchSessionResult.getResultValue()) {
+ SearchResults results =
+ searchJoinedStaticWithRuntimeAppFunctions(
+ searchSession, targetPackage, functionIdentifier);
+ results.getNextPage(
+ appSearchExecutor,
+ listAppSearchResult ->
+ callbackExecutor.execute(
+ () -> {
+ if (listAppSearchResult.isSuccess()) {
+ callback.onResult(
+ getEnabledStateFromSearchResults(
+ Objects.requireNonNull(
+ listAppSearchResult
+ .getResultValue())));
+ } else {
+ callback.onError(
+ failedResultToException(
+ listAppSearchResult));
+ }
+ }));
+ } catch (Exception e) {
+ callbackExecutor.execute(() -> callback.onError(e));
+ }
+ });
+ }
+
+ /**
+ * Searches joined app function static and runtime metadata using the function Id and the
+ * package.
+ *
+ * @hide
+ */
+ private static @NonNull SearchResults searchJoinedStaticWithRuntimeAppFunctions(
+ @NonNull GlobalSearchSession session,
+ @NonNull String targetPackage,
+ @NonNull String functionIdentifier) {
+ SearchSpec runtimeSearchSpec =
+ getAppFunctionRuntimeMetadataSearchSpecByFunctionId(targetPackage);
+ JoinSpec joinSpec =
+ new JoinSpec.Builder(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+ .setNestedSearch(functionIdentifier, runtimeSearchSpec)
+ .build();
+ SearchSpec joinedStaticWithRuntimeSearchSpec =
+ new SearchSpec.Builder()
+ .setJoinSpec(joinSpec)
+ .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+ .addFilterSchemas(
+ AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage(
+ targetPackage))
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build();
+ return session.search(functionIdentifier, joinedStaticWithRuntimeSearchSpec);
+ }
+
+ /**
+ * Finds whether the function is enabled or not from the search results returned by {@link
+ * #searchJoinedStaticWithRuntimeAppFunctions}.
+ *
+ * @throws IllegalArgumentException if the function is not found in the results
+ * @hide
+ */
+ private static boolean getEnabledStateFromSearchResults(
+ @NonNull List joinedStaticRuntimeResults) {
+ if (joinedStaticRuntimeResults.isEmpty()) {
+ // Function not found.
+ throw new IllegalArgumentException("App function not found.");
+ } else {
+ List runtimeMetadataResults =
+ joinedStaticRuntimeResults.getFirst().getJoinedResults();
+ if (!runtimeMetadataResults.isEmpty()) {
+ Boolean result =
+ (Boolean)
+ runtimeMetadataResults
+ .getFirst()
+ .getGenericDocument()
+ .getProperty(PROPERTY_ENABLED);
+ if (result != null) {
+ return result;
+ }
+ }
+ // Runtime metadata not found. Using the default value in the static metadata.
+ return joinedStaticRuntimeResults
+ .getFirst()
+ .getGenericDocument()
+ .getPropertyBoolean(STATIC_PROPERTY_ENABLED_BY_DEFAULT);
+ }
+ }
+
+ /**
+ * Returns search spec that queries app function metadata for a specific package name by its
+ * function identifier.
+ *
+ * @hide
+ */
+ public static @NonNull SearchSpec getAppFunctionRuntimeMetadataSearchSpecByFunctionId(
+ @NonNull String targetPackage) {
+ return new SearchSpec.Builder()
+ .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+ .addFilterSchemas(
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(targetPackage))
+ .addFilterProperties(
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(targetPackage),
+ List.of(AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID))
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build();
+ }
+
+ /**
+ * Converts a failed app search result codes into an exception.
+ *
+ * @hide
+ */
+ public static @NonNull Exception failedResultToException(
+ @NonNull AppSearchResult appSearchResult) {
+ return switch (appSearchResult.getResultCode()) {
+ case AppSearchResult.RESULT_INVALID_ARGUMENT ->
+ new IllegalArgumentException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_IO_ERROR ->
+ new IOException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_SECURITY_ERROR ->
+ new SecurityException(appSearchResult.getErrorMessage());
+ default -> new IllegalStateException(appSearchResult.getErrorMessage());
+ };
+ }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4bfae98e33d70204d83ba22b3de33b9bf7fb531
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Represents runtime function metadata of an app function.
+ *
+ * This is a temporary solution for app function indexing, as later we would like to index the
+ * actual function signature entity class shape instead of just the schema info.
+ *
+ * @hide
+ */
+// TODO(b/357551503): Link to canonical docs rather than duplicating once they
+// are available.
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionRuntimeMetadata extends GenericDocument {
+ public static final String RUNTIME_SCHEMA_TYPE = "AppFunctionRuntimeMetadata";
+ public static final String APP_FUNCTION_INDEXER_PACKAGE = "android";
+ public static final String APP_FUNCTION_RUNTIME_METADATA_DB = "appfunctions-db";
+ public static final String APP_FUNCTION_RUNTIME_NAMESPACE = "app_functions_runtime";
+ public static final String PROPERTY_FUNCTION_ID = "functionId";
+ public static final String PROPERTY_PACKAGE_NAME = "packageName";
+ public static final String PROPERTY_ENABLED = "enabled";
+ public static final String PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID =
+ "appFunctionStaticMetadataQualifiedId";
+ private static final String TAG = "AppSearchAppFunction";
+ private static final String RUNTIME_SCHEMA_TYPE_SEPARATOR = "-";
+
+ public AppFunctionRuntimeMetadata(@NonNull GenericDocument genericDocument) {
+ super(genericDocument);
+ }
+
+ /** Returns a per-app runtime metadata schema name, to store all functions for that package. */
+ public static String getRuntimeSchemaNameForPackage(@NonNull String pkg) {
+ return RUNTIME_SCHEMA_TYPE + RUNTIME_SCHEMA_TYPE_SEPARATOR + Objects.requireNonNull(pkg);
+ }
+
+ /** Returns the document id for an app function's runtime metadata. */
+ public static String getDocumentIdForAppFunction(
+ @NonNull String pkg, @NonNull String functionId) {
+ return pkg + "/" + functionId;
+ }
+
+ /**
+ * Different packages have different visibility requirements. To allow for different visibility,
+ * we need to have per-package app function schemas.
+ *
+ *
This schema should be set visible to callers from the package owner itself and for callers
+ * with {@link android.Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
+ * android.Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permissions.
+ *
+ * @param packageName The package name to create a schema for.
+ */
+ @NonNull
+ public static AppSearchSchema createAppFunctionRuntimeSchema(@NonNull String packageName) {
+ return getAppFunctionRuntimeSchemaBuilder(getRuntimeSchemaNameForPackage(packageName))
+ .addParentType(RUNTIME_SCHEMA_TYPE)
+ .build();
+ }
+
+ /**
+ * Creates a parent schema for all app function runtime schemas.
+ *
+ *
This schema should be set visible to the owner itself and for callers with {@link
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link
+ * android.permission.EXECUTE_APP_FUNCTIONS} permissions.
+ */
+ public static AppSearchSchema createParentAppFunctionRuntimeSchema() {
+ return getAppFunctionRuntimeSchemaBuilder(RUNTIME_SCHEMA_TYPE).build();
+ }
+
+ private static AppSearchSchema.Builder getAppFunctionRuntimeSchemaBuilder(
+ @NonNull String schemaType) {
+ return new AppSearchSchema.Builder(schemaType)
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_FUNCTION_ID)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_EXACT_TERMS)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_VERBATIM)
+ .build())
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_PACKAGE_NAME)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_EXACT_TERMS)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_VERBATIM)
+ .build())
+ .addProperty(
+ new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder(
+ PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setJoinableValueType(
+ AppSearchSchema.StringPropertyConfig
+ .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .build());
+ }
+
+ /** Returns the function id. This might look like "com.example.message#send_message". */
+ @NonNull
+ public String getFunctionId() {
+ return Objects.requireNonNull(getPropertyString(PROPERTY_FUNCTION_ID));
+ }
+
+ /** Returns the package name of the package that owns this function. */
+ @NonNull
+ public String getPackageName() {
+ return Objects.requireNonNull(getPropertyString(PROPERTY_PACKAGE_NAME));
+ }
+
+ /**
+ * Returns if the function is set to be enabled or not. If not set, the {@link
+ * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT} value would be used.
+ */
+ @Nullable
+ public Boolean getEnabled() {
+ return (Boolean) getProperty(PROPERTY_ENABLED);
+ }
+
+ /** Returns the qualified id linking to the static metadata of the app function. */
+ @Nullable
+ @VisibleForTesting
+ public String getAppFunctionStaticMetadataQualifiedId() {
+ return getPropertyString(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID);
+ }
+
+ public static final class Builder extends GenericDocument.Builder {
+ /**
+ * Creates a Builder for a {@link AppFunctionRuntimeMetadata}.
+ *
+ * @param packageName the name of the package that owns the function.
+ * @param functionId the id of the function.
+ * @param staticMetadataQualifiedId the qualified static metadata id that this runtime
+ * metadata refers to.
+ */
+ public Builder(
+ @NonNull String packageName,
+ @NonNull String functionId,
+ @NonNull String staticMetadataQualifiedId) {
+ super(
+ APP_FUNCTION_RUNTIME_NAMESPACE,
+ getDocumentIdForAppFunction(
+ Objects.requireNonNull(packageName),
+ Objects.requireNonNull(functionId)),
+ getRuntimeSchemaNameForPackage(packageName));
+ setPropertyString(PROPERTY_PACKAGE_NAME, packageName);
+ setPropertyString(PROPERTY_FUNCTION_ID, functionId);
+
+ // Set qualified id automatically
+ setPropertyString(
+ PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID, staticMetadataQualifiedId);
+ }
+
+ /**
+ * Sets an indicator specifying if the function is enabled or not. This would override the
+ * default enabled state in the static metadata ({@link
+ * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}).
+ */
+ @NonNull
+ public Builder setEnabled(boolean enabled) {
+ setPropertyBoolean(PROPERTY_ENABLED, enabled);
+ return this;
+ }
+
+ /** Creates the {@link AppFunctionRuntimeMetadata} GenericDocument. */
+ @NonNull
+ public AppFunctionRuntimeMetadata build() {
+ return new AppFunctionRuntimeMetadata(super.build());
+ }
+ }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index 6259d165d1324a73a7c4ef57512ebfc511b5fe8d..c27141a1acbf3cd3a6979db068a195efd54032bc 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -58,8 +58,7 @@ public abstract class AppFunctionService extends Service {
* applications can not abuse it.
*/
@NonNull
- public static final String SERVICE_INTERFACE =
- "android.app.appfunctions.AppFunctionService";
+ public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
private final Binder mBinder =
new IAppFunctionService.Stub() {
@@ -67,22 +66,20 @@ public abstract class AppFunctionService extends Service {
public void executeAppFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull IExecuteAppFunctionCallback callback) {
- if (AppFunctionService.this.checkCallingPermission(
- BIND_APP_FUNCTION_SERVICE) == PERMISSION_DENIED) {
+ if (AppFunctionService.this.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
+ == PERMISSION_DENIED) {
throw new SecurityException("Can only be called by the system server.");
}
SafeOneTimeExecuteAppFunctionCallback safeCallback =
new SafeOneTimeExecuteAppFunctionCallback(callback);
try {
- AppFunctionService.this.onExecuteFunction(
- request,
- safeCallback::onResult);
+ AppFunctionService.this.onExecuteFunction(request, safeCallback::onResult);
} catch (Exception ex) {
// Apps should handle exceptions. But if they don't, report the error on
// behalf of them.
safeCallback.onResult(
- new ExecuteAppFunctionResponse.Builder(
- getResultCode(ex), getExceptionMessage(ex)).build());
+ ExecuteAppFunctionResponse.newFailure(
+ getResultCode(ex), ex.getMessage(), /* extras= */ null));
}
}
};
@@ -110,15 +107,11 @@ public abstract class AppFunctionService extends Service {
* thread and dispatch the result with the given callback. You should always report back the
* result using the callback, no matter if the execution was successful or not.
*
- * @param request The function execution request.
+ * @param request The function execution request.
* @param callback A callback to report back the result.
*/
@MainThread
public abstract void onExecuteFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull Consumer callback);
-
- private String getExceptionMessage(Exception exception) {
- return exception.getMessage() == null ? "" : exception.getMessage();
- }
}
diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..926cc9a3642c7edeb206d0106771391502fb34a4
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.appsearch.util.DocumentIdUtil;
+
+import java.util.Objects;
+
+/**
+ * Contains constants and helper related to static metadata represented with {@code
+ * com.android.server.appsearch.appsindexer.appsearchtypes.AppFunctionStaticMetadata}.
+ *
+ * The constants listed here **must not change** and be kept consistent with the canonical static
+ * metadata class.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionStaticMetadataHelper {
+ public static final String STATIC_SCHEMA_TYPE = "AppFunctionStaticMetadata";
+ public static final String STATIC_PROPERTY_ENABLED_BY_DEFAULT = "enabledByDefault";
+
+ public static final String APP_FUNCTION_STATIC_NAMESPACE = "app_functions";
+
+ // These are constants that has to be kept the same with {@code
+ // com.android.server.appsearch.appsindexer.appsearchtypes.AppSearchHelper}.
+ public static final String APP_FUNCTION_STATIC_METADATA_DB = "apps-db";
+ public static final String APP_FUNCTION_INDEXER_PACKAGE = "android";
+
+ /** Returns a per-app static metadata schema name, to store all functions for that package. */
+ public static String getStaticSchemaNameForPackage(@NonNull String pkg) {
+ return STATIC_SCHEMA_TYPE + "-" + Objects.requireNonNull(pkg);
+ }
+
+ /** Returns the document id for an app function's static metadata. */
+ public static String getDocumentIdForAppFunction(
+ @NonNull String pkg, @NonNull String functionId) {
+ return pkg + "/" + functionId;
+ }
+
+ /**
+ * Returns the fully qualified Id used in AppSearch for the given package and function id app
+ * function static metadata.
+ */
+ public static String getStaticMetadataQualifiedId(String packageName, String functionId) {
+ return DocumentIdUtil.createQualifiedId(
+ APP_FUNCTION_INDEXER_PACKAGE,
+ APP_FUNCTION_STATIC_METADATA_DB,
+ APP_FUNCTION_STATIC_NAMESPACE,
+ getDocumentIdForAppFunction(packageName, functionId));
+ }
+}
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
index 2f3c555d9465bc8682ba0193a257e5123b9dcef9..e623fa10f4748dc7298b3168d518f4f8d091c296 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
@@ -23,7 +23,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
-
import java.util.Objects;
/**
@@ -40,8 +39,7 @@ public final class ExecuteAppFunctionAidlRequest implements Parcelable {
public ExecuteAppFunctionAidlRequest createFromParcel(Parcel in) {
ExecuteAppFunctionRequest clientRequest =
ExecuteAppFunctionRequest.CREATOR.createFromParcel(in);
- UserHandle userHandle =
- UserHandle.CREATOR.createFromParcel(in);
+ UserHandle userHandle = UserHandle.CREATOR.createFromParcel(in);
String callingPackage = in.readString8();
return new ExecuteAppFunctionAidlRequest(
clientRequest, userHandle, callingPackage);
@@ -53,19 +51,13 @@ public final class ExecuteAppFunctionAidlRequest implements Parcelable {
}
};
- /**
- * The client request to execute an app function.
- */
+ /** The client request to execute an app function. */
private final ExecuteAppFunctionRequest mClientRequest;
- /**
- * The user handle of the user to execute the app function.
- */
+ /** The user handle of the user to execute the app function. */
private final UserHandle mUserHandle;
- /**
- * The package name of the app that is requesting to execute the app function.
- */
+ /** The package name of the app that is requesting to execute the app function. */
private final String mCallingPackage;
public ExecuteAppFunctionAidlRequest(
@@ -87,25 +79,19 @@ public final class ExecuteAppFunctionAidlRequest implements Parcelable {
dest.writeString8(mCallingPackage);
}
- /**
- * Returns the client request to execute an app function.
- */
+ /** Returns the client request to execute an app function. */
@NonNull
public ExecuteAppFunctionRequest getClientRequest() {
return mClientRequest;
}
- /**
- * Returns the user handle of the user to execute the app function.
- */
+ /** Returns the user handle of the user to execute the app function. */
@NonNull
public UserHandle getUserHandle() {
return mUserHandle;
}
- /**
- * Returns the package name of the app that is requesting to execute the app function.
- */
+ /** Returns the package name of the app that is requesting to execute the app function. */
@NonNull
public String getCallingPackage() {
return mCallingPackage;
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
index db3de6261589e089b7a55ca4fd6fc9d51e800e13..fe7fd8837624ebfe57c46634e30af10608c58843 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -16,7 +16,6 @@
package android.app.appfunctions;
-
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import android.annotation.FlaggedApi;
@@ -28,9 +27,7 @@ import android.os.Parcelable;
import java.util.Objects;
-/**
- * A request to execute an app function.
- */
+/** A request to execute an app function. */
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public final class ExecuteAppFunctionRequest implements Parcelable {
@NonNull
@@ -40,8 +37,8 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
public ExecuteAppFunctionRequest createFromParcel(Parcel parcel) {
String targetPackageName = parcel.readString8();
String functionIdentifier = parcel.readString8();
- GenericDocumentWrapper parameters = GenericDocumentWrapper
- .CREATOR.createFromParcel(parcel);
+ GenericDocumentWrapper parameters =
+ GenericDocumentWrapper.CREATOR.createFromParcel(parcel);
Bundle extras = parcel.readBundle(Bundle.class.getClassLoader());
return new ExecuteAppFunctionRequest(
targetPackageName, functionIdentifier, extras, parameters);
@@ -52,34 +49,30 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
return new ExecuteAppFunctionRequest[size];
}
};
+
+ /** Returns the package name of the app that hosts the function. */
+ @NonNull private final String mTargetPackageName;
+
/**
- * Returns the package name of the app that hosts the function.
- */
- @NonNull
- private final String mTargetPackageName;
- /**
- * Returns the unique string identifier of the app function to be executed.
- * TODO(b/357551503): Document how callers can get the available function identifiers.
- */
- @NonNull
- private final String mFunctionIdentifier;
- /**
- * Returns additional metadata relevant to this function execution request.
+ * Returns the unique string identifier of the app function to be executed. TODO(b/357551503):
+ * Document how callers can get the available function identifiers.
*/
- @NonNull
- private final Bundle mExtras;
+ @NonNull private final String mFunctionIdentifier;
+
+ /** Returns additional metadata relevant to this function execution request. */
+ @NonNull private final Bundle mExtras;
+
/**
- * Returns the parameters required to invoke this function. Within this [GenericDocument],
- * the property names are the names of the function parameters and the property values are the
+ * Returns the parameters required to invoke this function. Within this [GenericDocument], the
+ * property names are the names of the function parameters and the property values are the
* values of those parameters.
*
*
The document may have missing parameters. Developers are advised to implement defensive
* handling measures.
- *
- * TODO(b/357551503): Document how function parameters can be obtained for function execution
+ *
+ *
TODO(b/357551503): Document how function parameters can be obtained for function execution
*/
- @NonNull
- private final GenericDocumentWrapper mParameters;
+ @NonNull private final GenericDocumentWrapper mParameters;
private ExecuteAppFunctionRequest(
@NonNull String targetPackageName,
@@ -92,17 +85,13 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
mParameters = Objects.requireNonNull(parameters);
}
- /**
- * Returns the package name of the app that hosts the function.
- */
+ /** Returns the package name of the app that hosts the function. */
@NonNull
public String getTargetPackageName() {
return mTargetPackageName;
}
- /**
- * Returns the unique string identifier of the app function to be executed.
- */
+ /** Returns the unique string identifier of the app function to be executed. */
@NonNull
public String getFunctionIdentifier() {
return mFunctionIdentifier;
@@ -111,8 +100,8 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
/**
* Returns the function parameters. The key is the parameter name, and the value is the
* parameter value.
- *
- * The bundle may have missing parameters. Developers are advised to implement defensive
+ *
+ *
The bundle may have missing parameters. Developers are advised to implement defensive
* handling measures.
*/
@NonNull
@@ -120,9 +109,7 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
return mParameters.getValue();
}
- /**
- * Returns the additional data relevant to this function execution.
- */
+ /** Returns the additional data relevant to this function execution. */
@NonNull
public Bundle getExtras() {
return mExtras;
@@ -141,37 +128,28 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
return 0;
}
- /**
- * Builder for {@link ExecuteAppFunctionRequest}.
- */
+ /** Builder for {@link ExecuteAppFunctionRequest}. */
public static final class Builder {
+ @NonNull private final String mTargetPackageName;
+ @NonNull private final String mFunctionIdentifier;
+ @NonNull private Bundle mExtras = Bundle.EMPTY;
+
@NonNull
- private final String mTargetPackageName;
- @NonNull
- private final String mFunctionIdentifier;
- @NonNull
- private Bundle mExtras = Bundle.EMPTY;
- @NonNull
- private GenericDocument mParameters =
- new GenericDocument.Builder<>("", "", "").build();
+ private GenericDocument mParameters = new GenericDocument.Builder<>("", "", "").build();
public Builder(@NonNull String targetPackageName, @NonNull String functionIdentifier) {
mTargetPackageName = Objects.requireNonNull(targetPackageName);
mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
}
- /**
- * Sets the additional data relevant to this function execution.
- */
+ /** Sets the additional data relevant to this function execution. */
@NonNull
public Builder setExtras(@NonNull Bundle extras) {
mExtras = Objects.requireNonNull(extras);
return this;
}
- /**
- * Sets the function parameters.
- */
+ /** Sets the function parameters. */
@NonNull
public Builder setParameters(@NonNull GenericDocument parameters) {
Objects.requireNonNull(parameters);
@@ -179,13 +157,13 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
return this;
}
- /**
- * Builds the {@link ExecuteAppFunctionRequest}.
- */
+ /** Builds the {@link ExecuteAppFunctionRequest}. */
@NonNull
public ExecuteAppFunctionRequest build() {
return new ExecuteAppFunctionRequest(
- mTargetPackageName, mFunctionIdentifier, mExtras,
+ mTargetPackageName,
+ mFunctionIdentifier,
+ mExtras,
new GenericDocumentWrapper(mParameters));
}
}
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index 9fb3375a2446100b0508cc19972cb01da3d2fb6c..f6580e63d7572e7dfbe0adde40537ee088ee6a53 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -31,9 +31,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-/**
- * The response to an app function execution.
- */
+/** The response to an app function execution. */
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public final class ExecuteAppFunctionResponse implements Parcelable {
@NonNull
@@ -43,10 +41,10 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
public ExecuteAppFunctionResponse createFromParcel(Parcel parcel) {
GenericDocumentWrapper resultWrapper =
Objects.requireNonNull(
- GenericDocumentWrapper
- .CREATOR.createFromParcel(parcel));
- Bundle extras = Objects.requireNonNull(
- parcel.readBundle(Bundle.class.getClassLoader()));
+ GenericDocumentWrapper.CREATOR.createFromParcel(parcel));
+ Bundle extras =
+ Objects.requireNonNull(
+ parcel.readBundle(Bundle.class.getClassLoader()));
int resultCode = parcel.readInt();
String errorMessage = parcel.readString8();
return new ExecuteAppFunctionResponse(
@@ -58,14 +56,15 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
return new ExecuteAppFunctionResponse[size];
}
};
+
/**
- * The name of the property that stores the function return value within the
- * {@code resultDocument}.
+ * The name of the property that stores the function return value within the {@code
+ * resultDocument}.
*
*
See {@link GenericDocument#getProperty(String)} for more information.
*
- *
If the function returns {@code void} or throws an error, the {@code resultDocument}
- * will be empty {@link GenericDocument}.
+ *
If the function returns {@code void} or throws an error, the {@code resultDocument} will
+ * be empty {@link GenericDocument}.
*
*
If the {@code resultDocument} is empty, {@link GenericDocument#getProperty(String)} will
* return {@code null}.
@@ -74,19 +73,13 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
*/
public static final String PROPERTY_RETURN_VALUE = "returnValue";
- /**
- * The call was successful.
- */
+ /** The call was successful. */
public static final int RESULT_OK = 0;
- /**
- * The caller does not have the permission to execute an app function.
- */
+ /** The caller does not have the permission to execute an app function. */
public static final int RESULT_DENIED = 1;
- /**
- * An unknown error occurred while processing the call in the AppFunctionService.
- */
+ /** An unknown error occurred while processing the call in the AppFunctionService. */
public static final int RESULT_APP_UNKNOWN_ERROR = 2;
/**
@@ -103,45 +96,36 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
*/
public static final int RESULT_INVALID_ARGUMENT = 4;
- /**
- * The operation was timed out.
- */
+ /** The operation was timed out. */
public static final int RESULT_TIMED_OUT = 5;
- /**
- * The result code of the app function execution.
- */
- @ResultCode
- private final int mResultCode;
+ /** The result code of the app function execution. */
+ @ResultCode private final int mResultCode;
/**
* The error message associated with the result, if any. This is {@code null} if the result code
* is {@link #RESULT_OK}.
*/
- @Nullable
- private final String mErrorMessage;
+ @Nullable private final String mErrorMessage;
/**
* Returns the return value of the executed function.
*
- *
The return value is stored in a {@link GenericDocument} with the key
- * {@link #PROPERTY_RETURN_VALUE}.
+ *
The return value is stored in a {@link GenericDocument} with the key {@link
+ * #PROPERTY_RETURN_VALUE}.
*
*
See {@link #getResultDocument} for more information on extracting the return value.
*/
- @NonNull
- private final GenericDocumentWrapper mResultDocumentWrapper;
+ @NonNull private final GenericDocumentWrapper mResultDocumentWrapper;
- /**
- * Returns the additional metadata data relevant to this function execution response.
- */
- @NonNull
- private final Bundle mExtras;
+ /** Returns the additional metadata data relevant to this function execution response. */
+ @NonNull private final Bundle mExtras;
- private ExecuteAppFunctionResponse(@NonNull GenericDocumentWrapper resultDocumentWrapper,
- @NonNull Bundle extras,
- @ResultCode int resultCode,
- @Nullable String errorMessage) {
+ private ExecuteAppFunctionResponse(
+ @NonNull GenericDocumentWrapper resultDocumentWrapper,
+ @NonNull Bundle extras,
+ @ResultCode int resultCode,
+ @Nullable String errorMessage) {
mResultDocumentWrapper = Objects.requireNonNull(resultDocumentWrapper);
mExtras = Objects.requireNonNull(extras);
mResultCode = resultCode;
@@ -161,15 +145,61 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR;
}
+ /**
+ * Returns a successful response.
+ *
+ * @param resultDocument The return value of the executed function.
+ * @param extras The additional metadata data relevant to this function execution response.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static ExecuteAppFunctionResponse newSuccess(
+ @NonNull GenericDocument resultDocument, @Nullable Bundle extras) {
+ Objects.requireNonNull(resultDocument);
+ Bundle actualExtras = getActualExtras(extras);
+ GenericDocumentWrapper resultDocumentWrapper = new GenericDocumentWrapper(resultDocument);
+
+ return new ExecuteAppFunctionResponse(
+ resultDocumentWrapper, actualExtras, RESULT_OK, /* errorMessage= */ null);
+ }
+
+ /**
+ * Returns a failure response.
+ *
+ * @param resultCode The result code of the app function execution.
+ * @param extras The additional metadata data relevant to this function execution response.
+ * @param errorMessage The error message associated with the result, if any.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static ExecuteAppFunctionResponse newFailure(
+ @ResultCode int resultCode, @Nullable String errorMessage, @Nullable Bundle extras) {
+ if (resultCode == RESULT_OK) {
+ throw new IllegalArgumentException("resultCode must not be RESULT_OK");
+ }
+ Bundle actualExtras = getActualExtras(extras);
+ GenericDocumentWrapper emptyWrapper =
+ new GenericDocumentWrapper(new GenericDocument.Builder<>("", "", "").build());
+ return new ExecuteAppFunctionResponse(emptyWrapper, actualExtras, resultCode, errorMessage);
+ }
+
+ private static Bundle getActualExtras(@Nullable Bundle extras) {
+ if (extras == null) {
+ return Bundle.EMPTY;
+ }
+ return extras;
+ }
+
/**
* Returns a generic document containing the return value of the executed function.
*
- *
The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
+ * The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
*
*
An empty document is returned if {@link #isSuccess} is {@code false} or if the executed
* function does not produce a return value.
*
*
Sample code for extracting the return value:
+ *
*
* GenericDocument resultDocument = response.getResultDocument();
* Object returnValue = resultDocument.getProperty(PROPERTY_RETURN_VALUE);
@@ -185,17 +215,15 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
return mResultDocumentWrapper.getValue();
}
- /**
- * Returns the extras of the app function execution.
- */
+ /** Returns the extras of the app function execution. */
@NonNull
public Bundle getExtras() {
return mExtras;
}
/**
- * Returns {@code true} if {@link #getResultCode} equals
- * {@link ExecuteAppFunctionResponse#RESULT_OK}.
+ * Returns {@code true} if {@link #getResultCode} equals {@link
+ * ExecuteAppFunctionResponse#RESULT_OK}.
*/
public boolean isSuccess() {
return getResultCode() == RESULT_OK;
@@ -240,74 +268,13 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
@IntDef(
prefix = {"RESULT_"},
value = {
- RESULT_OK,
- RESULT_DENIED,
- RESULT_APP_UNKNOWN_ERROR,
- RESULT_INTERNAL_ERROR,
- RESULT_INVALID_ARGUMENT,
- RESULT_TIMED_OUT,
+ RESULT_OK,
+ RESULT_DENIED,
+ RESULT_APP_UNKNOWN_ERROR,
+ RESULT_INTERNAL_ERROR,
+ RESULT_INVALID_ARGUMENT,
+ RESULT_TIMED_OUT,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface ResultCode {
- }
-
- /**
- * The builder for creating {@link ExecuteAppFunctionResponse} instances.
- */
- @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
- public static final class Builder {
- @NonNull
- private GenericDocument mResultDocument =
- new GenericDocument.Builder<>("", "", "").build();
- @NonNull
- private Bundle mExtras = Bundle.EMPTY;
- private int mResultCode;
- @Nullable
- private String mErrorMessage;
-
- /**
- * Creates a new builder for {@link ExecuteAppFunctionResponse}.
- */
- private Builder() {
- }
-
- /**
- * Creates a new builder for {@link ExecuteAppFunctionResponse} to build a success response
- * with a result code of {@link #RESULT_OK} and a resultDocument.
- */
- public Builder(@NonNull GenericDocument resultDocument) {
- Objects.requireNonNull(resultDocument);
- mResultDocument = resultDocument;
- mResultCode = RESULT_OK;
- }
-
- /**
- * Creates a new builder for {@link ExecuteAppFunctionResponse} to build an error response
- * with a result code and an error message.
- */
- public Builder(@ResultCode int resultCode,
- @NonNull String errorMessage) {
- mResultCode = resultCode;
- mErrorMessage = Objects.requireNonNull(errorMessage);
- }
-
- /**
- * Sets the extras of the app function execution.
- */
- @NonNull
- public Builder setExtras(@NonNull Bundle extras) {
- mExtras = Objects.requireNonNull(extras);
- return this;
- }
-
- /**
- * Builds the {@link ExecuteAppFunctionResponse} instance.
- */
- @NonNull
- public ExecuteAppFunctionResponse build() {
- return new ExecuteAppFunctionResponse(
- new GenericDocumentWrapper(mResultDocument),
- mExtras, mResultCode, mErrorMessage);
- }
- }
+ public @interface ResultCode {}
}
diff --git a/core/java/android/app/appfunctions/GenericDocumentWrapper.java b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
index 8c76c8e4b73b38d4a87c6203b9e27e2eaca1edf8..84b1837f4a2fcae666556984f10184d84fb6d99c 100644
--- a/core/java/android/app/appfunctions/GenericDocumentWrapper.java
+++ b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
@@ -56,16 +56,13 @@ public final class GenericDocumentWrapper implements Parcelable {
return new GenericDocumentWrapper[size];
}
};
- @NonNull
- private final GenericDocument mGenericDocument;
+ @NonNull private final GenericDocument mGenericDocument;
public GenericDocumentWrapper(@NonNull GenericDocument genericDocument) {
mGenericDocument = Objects.requireNonNull(genericDocument);
}
- /**
- * Returns the wrapped {@link android.app.appsearch.GenericDocument}
- */
+ /** Returns the wrapped {@link android.app.appsearch.GenericDocument} */
@NonNull
public GenericDocument getValue() {
return mGenericDocument;
@@ -86,6 +83,5 @@ public final class GenericDocumentWrapper implements Parcelable {
} finally {
parcel.recycle();
}
-
}
}
diff --git a/core/java/android/app/jank/OWNERS b/core/java/android/app/jank/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..806de574b071537410fc2effa61bceca294fabe4
--- /dev/null
+++ b/core/java/android/app/jank/OWNERS
@@ -0,0 +1,4 @@
+steventerrell@google.com
+carmenjackson@google.com
+jjaggi@google.com
+pmuetschard@google.com
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 606ca3393de018eefd4f4aa44effde9d8213e9ae..9891e8930936d748ec633b9b9e754d84f0419c9e 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -127,6 +127,16 @@ flag {
bug: "241732519"
}
+flag {
+ name: "notif_channel_crop_vibration_effects"
+ namespace: "systemui"
+ description: "Limits the size of vibration effects that can be stored in a NotificationChannel"
+ bug: "345881518"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
flag {
name: "evenly_divided_call_style_action_layout"
namespace: "systemui"
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
new file mode 100644
index 0000000000000000000000000000000000000000..8d25cad2fc673675e1dd9e7d960e528364775e0d
--- /dev/null
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.supervision;
+
+/**
+ * Internal IPC interface to the supervision service.
+ * {@hide}
+ */
+interface ISupervisionManager {
+ boolean isSupervisionEnabled();
+}
diff --git a/core/java/android/app/supervision/OWNERS b/core/java/android/app/supervision/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..afc549517abee27c3016794617597b5429b69f49
--- /dev/null
+++ b/core/java/android/app/supervision/OWNERS
@@ -0,0 +1,2 @@
+jparks@google.com
+romkal@google.com
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..8611a92074c006df0e746e17b566254852f2723a
--- /dev/null
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.supervision;
+
+import android.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * Service for handling parental supervision.
+ *
+ * @hide
+ */
+@SystemService(Context.SUPERVISION_SERVICE)
+public class SupervisionManager {
+ private final Context mContext;
+ private final ISupervisionManager mService;
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public SupervisionManager(Context context, ISupervisionManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Returns whether the device is supervised.
+ *
+ * @hide
+ */
+ public boolean isSupervisionEnabled() {
+ try {
+ return mService.isSupervisionEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+}
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
new file mode 100644
index 0000000000000000000000000000000000000000..bcb5b3636c958c0f921886a7cb72c2fe0ed02e40
--- /dev/null
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -0,0 +1,10 @@
+package: "android.app.supervision.flags"
+container: "system"
+
+flag {
+ name: "supervision_api"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag to enable the SupervisionService"
+ bug: "340351729"
+}
\ No newline at end of file
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 4e0379e3dc3a61e2b24e16ab7fcc2f0166b4e282..7117f250d1d5b34efb2ce8676ce79372820f5b07 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -57,3 +57,10 @@ flag {
description: "Enable support for persisting RemoteViews previews to Protobuf"
bug: "306546610"
}
+
+flag {
+ name: "remote_document_support"
+ namespace: "app_widgets"
+ description: "Remote document support features in Q2 2025 release"
+ bug: "339721781"
+}
\ No newline at end of file
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index ee9114f74092e22738d57efb25b70dc27cec9c21..93d62cfeb537896d6f699ccd5263863385663928 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -9,13 +9,6 @@ flag {
bug: "296251481"
}
-flag {
- name: "companion_transport_apis"
- namespace: "companion"
- description: "Grants access to the companion transport apis."
- bug: "288297505"
-}
-
flag {
name: "association_tag"
is_exported: true
diff --git a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
index 7c674f9cde6b981b2c66e307b3b1bb7e15aadb1d..767f52a92566ebb4674fc326aec09c12f55dc34c 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
@@ -54,4 +54,13 @@ oneway interface IVirtualDeviceActivityListener {
*/
void onActivityLaunchBlocked(int displayId, in ComponentName componentName, in UserHandle user,
in IntentSender intentSender);
+
+ /**
+ * Called when a secure surface is shown on the device.
+ *
+ * @param displayId The display ID on which the secure surface was shown.
+ * @param componentName The component name of the activity that showed the secure surface.
+ * @param user The user associated with the activity.
+ */
+ void onSecureWindowShown(int displayId, in ComponentName componentName, in UserHandle user);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index b7bf2d16ba2c41eb8859423bfd01930966011d91..de20a68e52cb8927c0ba425bf023abcf83c1d522 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -151,7 +151,24 @@ public class VirtualDeviceInternal {
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void onSecureWindowShown(int displayId, ComponentName componentName,
+ UserHandle user) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mActivityListenersLock) {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i)
+ .onSecureWindowShown(displayId, componentName, user);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
+
private final IVirtualDeviceSoundEffectListener mSoundEffectListener =
new IVirtualDeviceSoundEffectListener.Stub() {
@Override
@@ -584,6 +601,12 @@ public class VirtualDeviceInternal {
mActivityListener.onActivityLaunchBlocked(
displayId, componentName, user, intentSender));
}
+
+ public void onSecureWindowShown(int displayId, ComponentName componentName,
+ UserHandle user) {
+ mExecutor.execute(() ->
+ mActivityListener.onSecureWindowShown(displayId, componentName, user));
+ }
}
/**
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 40aa6837ad1d3f551bfed19bdd61dd71f9a9f7ea..473ab27ee5605c650bb0dcfd5a371b1d8d81c36b 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -1255,6 +1255,22 @@ public final class VirtualDeviceManager {
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
default void onActivityLaunchBlocked(int displayId, @NonNull ComponentName componentName,
@NonNull UserHandle user, @Nullable IntentSender intentSender) {}
+
+ /**
+ * Called when a window with a secure surface is shown on the device.
+ *
+ * Note that this is called only when the window is associated with an activity.
+ *
+ * @param displayId The display ID on which the window was shown.
+ * @param componentName The component name of the activity that showed the window.
+ * @param user The user associated with the activity.
+ *
+ * @see Display#FLAG_SECURE
+ * @see WindowManager.LayoutParams#FLAG_SECURE
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+ default void onSecureWindowShown(int displayId, @NonNull ComponentName componentName,
+ @NonNull UserHandle user) {}
}
/**
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index c3c3f0ef32e141c2dc778e4387e7271a6403307d..748260bc8d5fd85f09eb5a433cb6e8ce8fae918f 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -50,6 +50,7 @@ flag {
name: "activity_control_api"
description: "Enable APIs for fine grained activity policy, fallback and callbacks"
bug: "333443509"
+ is_exported: true
}
flag {
@@ -97,9 +98,31 @@ flag {
}
flag {
- name: "camera_multiple_input_streams"
- is_exported: true
- namespace: "virtual_devices"
- description: "Expose multiple surface for the virtual camera owner for different stream resolution"
- bug: "341083465"
+ name: "camera_multiple_input_streams"
+ is_exported: true
+ namespace: "virtual_devices"
+ description: "Expose multiple surface for the virtual camera owner for different stream resolution"
+ bug: "341083465"
+}
+
+flag {
+ namespace: "virtual_devices"
+ name: "device_aware_display_power"
+ description: "Device awareness in power and display APIs"
+ bug: "285020111"
+}
+
+flag {
+ namespace: "virtual_devices"
+ name: "display_power_manager_apis"
+ description: "Make relevant PowerManager APIs display aware by default"
+ bug: "365042486"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "status_bar_and_insets"
+ namespace: "virtual_devices"
+ description: "Allow for status bar and insets on virtual devices"
+ bug: "350007866"
}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 21ad914bbc29e448cffb8b2efefba8c2e49e464a..68bc9bce28d23d6d82820756a0143487b8862be7 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -17,17 +17,27 @@
package android.companion.virtual.sensor;
+import static android.hardware.Sensor.REPORTING_MODE_CONTINUOUS;
+import static android.hardware.Sensor.REPORTING_MODE_ONE_SHOT;
+import static android.hardware.Sensor.REPORTING_MODE_ON_CHANGE;
+import static android.hardware.Sensor.REPORTING_MODE_SPECIAL_TRIGGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.companion.virtualdevice.flags.Flags;
import android.hardware.Sensor;
import android.hardware.SensorDirectChannel;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -42,6 +52,13 @@ import java.util.Objects;
public final class VirtualSensorConfig implements Parcelable {
private static final String TAG = "VirtualSensorConfig";
+ // Defined in sensors.h
+ private static final int FLAG_WAKE_UP_SENSOR = 1;
+
+ // Mask for the reporting mode, bit 2, 3, 4.
+ private static final int REPORTING_MODE_MASK = 0xE;
+ private static final int REPORTING_MODE_SHIFT = 1;
+
// Mask for direct mode highest rate level, bit 7, 8, 9.
private static final int DIRECT_REPORT_MASK = 0x380;
private static final int DIRECT_REPORT_SHIFT = 7;
@@ -62,6 +79,17 @@ public final class VirtualSensorConfig implements Parcelable {
private final int mFlags;
+ /** @hide */
+ @IntDef(prefix = "REPORTING_MODE_", value = {
+ REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_ON_CHANGE,
+ REPORTING_MODE_ONE_SHOT,
+ REPORTING_MODE_SPECIAL_TRIGGER
+ })
+
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReportingMode {}
+
private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor,
float maximumRange, float resolution, float power, int minDelay, int maxDelay,
int flags) {
@@ -193,8 +221,7 @@ public final class VirtualSensorConfig implements Parcelable {
@SensorDirectChannel.RateLevel
public int getHighestDirectReportRateLevel() {
int rateLevel = ((mFlags & DIRECT_REPORT_MASK) >> DIRECT_REPORT_SHIFT);
- return rateLevel <= SensorDirectChannel.RATE_VERY_FAST
- ? rateLevel : SensorDirectChannel.RATE_VERY_FAST;
+ return Math.min(rateLevel, SensorDirectChannel.RATE_VERY_FAST);
}
/**
@@ -214,6 +241,28 @@ public final class VirtualSensorConfig implements Parcelable {
return memoryTypes;
}
+ /**
+ * Returns whether the sensor is a wake-up sensor.
+ *
+ * @see Builder#setWakeUpSensor(boolean)
+ * @see Sensor#isWakeUpSensor()
+ */
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ public boolean isWakeUpSensor() {
+ return (mFlags & FLAG_WAKE_UP_SENSOR) > 0;
+ }
+
+ /**
+ * Returns the reporting mode of this sensor.
+ *
+ * @see Builder#setReportingMode(int)
+ * @see Sensor#getReportingMode()
+ */
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ public @ReportingMode int getReportingMode() {
+ return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
+ }
+
/**
* Returns the sensor flags.
*
@@ -383,6 +432,45 @@ public final class VirtualSensorConfig implements Parcelable {
}
return this;
}
+
+ /**
+ * Sets whether this sensor is a wake up sensor.
+ *
+ * @see Sensor#isWakeUpSensor()
+ */
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @NonNull
+ public VirtualSensorConfig.Builder setWakeUpSensor(boolean wakeUpSensor) {
+ if (wakeUpSensor) {
+ mFlags |= FLAG_WAKE_UP_SENSOR;
+ } else {
+ mFlags &= ~FLAG_WAKE_UP_SENSOR;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the reporting mode of this sensor.
+ *
+ * @throws IllegalArgumentException if the reporting mode is not one of
+ * {@link Sensor#REPORTING_MODE_CONTINUOUS}, {@link Sensor#REPORTING_MODE_ON_CHANGE},
+ * {@link Sensor#REPORTING_MODE_ONE_SHOT}, or
+ * {@link Sensor#REPORTING_MODE_SPECIAL_TRIGGER}.
+ *
+ * @see Sensor#getReportingMode()
+ */
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @NonNull
+ public VirtualSensorConfig.Builder setReportingMode(@ReportingMode int reportingMode) {
+ if (reportingMode != REPORTING_MODE_CONTINUOUS
+ && reportingMode != REPORTING_MODE_ON_CHANGE
+ && reportingMode != REPORTING_MODE_ONE_SHOT
+ && reportingMode != REPORTING_MODE_SPECIAL_TRIGGER) {
+ throw new IllegalArgumentException("Invalid reporting mode: " + reportingMode);
+ }
+ mFlags |= reportingMode << REPORTING_MODE_SHIFT;
+ return this;
+ }
}
@NonNull
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 37f419d717c2f4dd1ee01391e68b756831a231f8..ffed5366d31b9e30b93e460d8724b229f988af34 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -629,11 +629,10 @@ public final class AttributionSource implements Parcelable {
* A builder for {@link AttributionSource}
*/
public static final class Builder {
+ private boolean mHasBeenUsed;
private @NonNull final AttributionSourceState mAttributionSourceState =
new AttributionSourceState();
- private long mBuilderFieldsSet = 0L;
-
/**
* Creates a new Builder.
*
@@ -642,8 +641,17 @@ public final class AttributionSource implements Parcelable {
*/
public Builder(int uid) {
mAttributionSourceState.uid = uid;
+ mAttributionSourceState.pid = Process.INVALID_PID;
+ mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
+ mAttributionSourceState.token = sDefaultToken;
}
+ /**
+ * Creates a builder that is ready to build a new {@link AttributionSource} where
+ * all fields (primitive, immutable data, pointers) are copied from the given
+ * {@link AttributionSource}. Builder methods can still be used to mutate fields further.
+ * @param current The source to copy fields from.
+ */
public Builder(@NonNull AttributionSource current) {
if (current == null) {
throw new IllegalArgumentException("current AttributionSource can not be null");
@@ -652,9 +660,11 @@ public final class AttributionSource implements Parcelable {
mAttributionSourceState.pid = current.getPid();
mAttributionSourceState.packageName = current.getPackageName();
mAttributionSourceState.attributionTag = current.getAttributionTag();
- mAttributionSourceState.token = current.getToken();
mAttributionSourceState.renouncedPermissions =
current.mAttributionSourceState.renouncedPermissions;
+ mAttributionSourceState.deviceId = current.getDeviceId();
+ mAttributionSourceState.next = current.mAttributionSourceState.next;
+ mAttributionSourceState.token = current.getToken();
}
/**
@@ -666,7 +676,6 @@ public final class AttributionSource implements Parcelable {
*/
public @NonNull Builder setPid(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x2;
mAttributionSourceState.pid = value;
return this;
}
@@ -676,7 +685,6 @@ public final class AttributionSource implements Parcelable {
*/
public @NonNull Builder setPackageName(@Nullable String value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x4;
mAttributionSourceState.packageName = value;
return this;
}
@@ -686,7 +694,6 @@ public final class AttributionSource implements Parcelable {
*/
public @NonNull Builder setAttributionTag(@Nullable String value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x8;
mAttributionSourceState.attributionTag = value;
return this;
}
@@ -717,11 +724,11 @@ public final class AttributionSource implements Parcelable {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
- public @NonNull Builder setRenouncedPermissions(@Nullable Set value) {
+ public @NonNull Builder setRenouncedPermissions(
+ @Nullable Set renouncedPermissions) {
checkNotUsed();
- mBuilderFieldsSet |= 0x10;
- mAttributionSourceState.renouncedPermissions = (value != null)
- ? value.toArray(new String[0]) : null;
+ mAttributionSourceState.renouncedPermissions = (renouncedPermissions != null)
+ ? renouncedPermissions.toArray(new String[0]) : null;
return this;
}
@@ -734,7 +741,6 @@ public final class AttributionSource implements Parcelable {
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public @NonNull Builder setDeviceId(int deviceId) {
checkNotUsed();
- mBuilderFieldsSet |= 0x12;
mAttributionSourceState.deviceId = deviceId;
return this;
}
@@ -744,7 +750,6 @@ public final class AttributionSource implements Parcelable {
*/
public @NonNull Builder setNext(@Nullable AttributionSource value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x20;
mAttributionSourceState.next = (value != null) ? new AttributionSourceState[]
{value.mAttributionSourceState} : mAttributionSourceState.next;
return this;
@@ -759,7 +764,6 @@ public final class AttributionSource implements Parcelable {
if (value == null) {
throw new IllegalArgumentException("Null AttributionSource not permitted.");
}
- mBuilderFieldsSet |= 0x20;
mAttributionSourceState.next =
new AttributionSourceState[]{value.mAttributionSourceState};
return this;
@@ -768,28 +772,7 @@ public final class AttributionSource implements Parcelable {
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull AttributionSource build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x40; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x2) == 0) {
- mAttributionSourceState.pid = Process.INVALID_PID;
- }
- if ((mBuilderFieldsSet & 0x4) == 0) {
- mAttributionSourceState.packageName = null;
- }
- if ((mBuilderFieldsSet & 0x8) == 0) {
- mAttributionSourceState.attributionTag = null;
- }
- if ((mBuilderFieldsSet & 0x10) == 0) {
- mAttributionSourceState.renouncedPermissions = null;
- }
- if ((mBuilderFieldsSet & 0x12) == 0) {
- mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
- }
- if ((mBuilderFieldsSet & 0x20) == 0) {
- mAttributionSourceState.next = null;
- }
-
- mAttributionSourceState.token = sDefaultToken;
+ mHasBeenUsed = true;
if (mAttributionSourceState.next == null) {
// The NDK aidl backend doesn't support null parcelable arrays.
@@ -799,7 +782,7 @@ public final class AttributionSource implements Parcelable {
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x40) != 0) {
+ if (mHasBeenUsed) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9c711bcb15214e4b9c2042bf37a7c688ef98f9da..12c5d0756fc9c7f262819b0ec517d98087b63e4e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -33,6 +33,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PermissionMethod;
import android.annotation.PermissionName;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.StringDef;
import android.annotation.StringRes;
@@ -6678,6 +6679,8 @@ public abstract class Context {
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.webkit.WebViewUpdateManager} for accessing the WebView update service.
*
+ * This can only be used on devices with {@link PackageManager#FEATURE_WEBVIEW}.
+ *
* @see #getSystemService(String)
* @see android.webkit.WebViewUpdateManager
* @hide
@@ -6685,6 +6688,7 @@ public abstract class Context {
@FlaggedApi(android.webkit.Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SuppressLint("ServiceName")
+ @RequiresFeature(PackageManager.FEATURE_WEBVIEW)
public static final String WEBVIEW_UPDATE_SERVICE = "webviewupdate";
/**
@@ -6710,6 +6714,16 @@ public abstract class Context {
*/
public static final String PROTOLOG_CONFIGURATION_SERVICE = "protolog_configuration";
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.supervision.SupervisionManager}.
+ *
+ * @see #getSystemService(String)
+ * @see android.app.supervision.SupervisionManager
+ * @hide
+ */
+ public static final String SUPERVISION_SERVICE = "supervision";
+
/**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index abb0d8d88cceba961b5263fbe2e881d4c7fa9d03..031380dc1962a5fa26538158f54a327ae5d2a611 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -86,6 +86,7 @@ import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.XmlUtils;
+import com.android.modules.expresslog.Counter;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -7649,13 +7650,6 @@ public class Intent implements Parcelable, Cloneable {
| FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| FLAG_GRANT_PREFIX_URI_PERMISSION;
- /**
- * Flags that are not normally set by application code, but set for you by the system.
- */
- private static final int SYSTEM_ONLY_FLAGS = FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
- | FLAG_ACTIVITY_BROUGHT_TO_FRONT
- | FLAG_RECEIVER_FROM_SHELL;
-
/**
* Local flag indicating this instance was created by copy constructor.
*/
@@ -7709,11 +7703,6 @@ public class Intent implements Parcelable, Cloneable {
@TestApi
public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0;
- /**
- * Extended flags that are not normally set by application code, but set for you by the system.
- */
- private static final int SYSTEM_ONLY_EXTENDED_FLAGS = EXTENDED_FLAG_FILTER_MISMATCH;
-
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// toUri() and parseUri() options.
@@ -12657,28 +12646,6 @@ public class Intent implements Parcelable, Cloneable {
}
}
- /**
- * Prepare this {@link Intent} to enter system_server.
- *
- * @hide
- */
- public void prepareToEnterSystemServer() {
- // Refuse possible leaked file descriptors
- if (hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- // These flags are set only by the system, and should be stripped out as soon as the intent
- // is received by system_server from the caller so it can be properly updated later.
- removeFlags(SYSTEM_ONLY_FLAGS);
- removeExtendedFlags(SYSTEM_ONLY_EXTENDED_FLAGS);
- if (mOriginalIntent != null) {
- mOriginalIntent.prepareToEnterSystemServer();
- }
- if (mSelector != null) {
- mSelector.prepareToEnterSystemServer();
- }
- }
-
/** @hide */
public boolean hasWebURI() {
if (getData() == null) {
@@ -12839,6 +12806,8 @@ public class Intent implements Parcelable, Cloneable {
new ClipData.Item(text, htmlText, null, stream));
setClipData(clipData);
if (stream != null) {
+ logCounterIfFlagsMissing(FLAG_GRANT_READ_URI_PERMISSION,
+ "intents.value_explicit_uri_grant_for_send_action");
addFlags(FLAG_GRANT_READ_URI_PERMISSION);
}
return true;
@@ -12880,6 +12849,8 @@ public class Intent implements Parcelable, Cloneable {
setClipData(clipData);
if (streams != null) {
+ logCounterIfFlagsMissing(FLAG_GRANT_READ_URI_PERMISSION,
+ "intents.value_explicit_uri_grant_for_send_multiple_action");
addFlags(FLAG_GRANT_READ_URI_PERMISSION);
}
return true;
@@ -12899,6 +12870,10 @@ public class Intent implements Parcelable, Cloneable {
putExtra(MediaStore.EXTRA_OUTPUT, output);
setClipData(ClipData.newRawUri("", output));
+
+ logCounterIfFlagsMissing(
+ FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_READ_URI_PERMISSION,
+ "intents.value_explicit_uri_grant_for_image_capture_action");
addFlags(FLAG_GRANT_WRITE_URI_PERMISSION|FLAG_GRANT_READ_URI_PERMISSION);
return true;
}
@@ -12907,6 +12882,12 @@ public class Intent implements Parcelable, Cloneable {
return false;
}
+ private void logCounterIfFlagsMissing(int requiredFlags, String metricId) {
+ if ((getFlags() & requiredFlags) != requiredFlags) {
+ Counter.logIncrement(metricId);
+ }
+ }
+
@android.ravenwood.annotation.RavenwoodThrow
private Uri maybeConvertFileToContentUri(Context context, Uri uri) {
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 495ae6026805254bd558d8337bdf204e268c247c..34bea1a4df6f4beabc6c3bb12e6af26032a190f5 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -849,6 +849,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/
public static final int PRIVATE_FLAG_EXT_CPU_OVERRIDE = 1 << 5;
+ /**
+ * Whether the app has been previously not launched
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EXT_NOT_LAUNCHED = 1 << 6;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = {
PRIVATE_FLAG_EXT_PROFILEABLE,
@@ -857,6 +863,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK,
PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS,
PRIVATE_FLAG_EXT_CPU_OVERRIDE,
+ PRIVATE_FLAG_EXT_NOT_LAUNCHED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlagsExt {}
@@ -2663,6 +2670,22 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY) != 0;
}
+ /**
+ * Returns whether the app in the STOPPED state.
+ * @hide
+ */
+ public boolean isStopped() {
+ return (flags & ApplicationInfo.FLAG_STOPPED) != 0;
+ }
+
+ /**
+ * Returns whether the app was never launched (any process started) before.
+ * @hide
+ */
+ public boolean isNotLaunched() {
+ return (privateFlagsExt & ApplicationInfo.PRIVATE_FLAG_EXT_NOT_LAUNCHED) != 0;
+ }
+
/**
* Checks if a changeId is enabled for the current user
* @param changeId The changeId to verify
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 3a33ef9002cc9b3ae0a3b4213711b5bb70e35cd7..9eec7a4e8f71ec2431178557798ee4c7f354d134 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -160,6 +160,16 @@ flag {
}
}
+flag {
+ name: "fix_avatar_content_provider_null_authority"
+ namespace: "multiuser"
+ description: "Fix crash when content provider authority is null."
+ bug: "362880068"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
flag {
name: "fix_avatar_picker_not_responding_for_new_user"
namespace: "multiuser"
@@ -416,4 +426,13 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+
+flag {
+ name: "caching_development_improvements"
+ namespace: "multiuser"
+ description: "System API to simplify caching implamentations"
+ bug: "364947162"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index d128055fec6d4139c39b4c8ce4ccec5e03d8355d..a20159da699cfd6c673378cbaea7c3ad33774480 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -19,8 +19,6 @@ package android.content.rollback;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
-import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Parcel;
@@ -136,11 +134,8 @@ public final class RollbackInfo implements Parcelable {
* Get rollback impact level. Refer {@link
* android.content.pm.PackageInstaller.SessionParams#setRollbackImpactLevel(int)} for more info
* on impact level.
- *
- * @hide
*/
- @TestApi
- @FlaggedApi(Flags.FLAG_RECOVERABILITY_DETECTION)
+ @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
public @PackageManager.RollbackImpactLevel int getRollbackImpactLevel() {
return mRollbackImpactLevel;
}
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 65148726224ed7becfc7082ac32ac59668bc917a..ef59e0af3a277d10104474ea75814381b36404bc 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -26,6 +26,10 @@ import android.database.sqlite.SQLiteClosable;
import android.database.sqlite.SQLiteException;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodThrow;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
@@ -40,9 +44,8 @@ import dalvik.system.CloseGuard;
* consumer for reading.
*
*/
-@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.CursorWindow_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("CursorWindow_host")
public class CursorWindow extends SQLiteClosable implements Parcelable {
private static final String STATS_TAG = "CursorWindowStats";
@@ -63,48 +66,69 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
private final CloseGuard mCloseGuard;
// May throw CursorWindowAllocationException
+ @RavenwoodRedirect
private static native long nativeCreate(String name, int cursorWindowSize);
// May throw CursorWindowAllocationException
+ @RavenwoodRedirect
private static native long nativeCreateFromParcel(Parcel parcel);
+ @RavenwoodRedirect
private static native void nativeDispose(long windowPtr);
+ @RavenwoodRedirect
private static native void nativeWriteToParcel(long windowPtr, Parcel parcel);
+ @RavenwoodRedirect
private static native String nativeGetName(long windowPtr);
+ @RavenwoodRedirect
private static native byte[] nativeGetBlob(long windowPtr, int row, int column);
+ @RavenwoodRedirect
private static native String nativeGetString(long windowPtr, int row, int column);
+ @RavenwoodThrow
private static native void nativeCopyStringToBuffer(long windowPtr, int row, int column,
CharArrayBuffer buffer);
+ @RavenwoodRedirect
private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column);
+ @RavenwoodRedirect
private static native boolean nativePutString(long windowPtr, String value,
int row, int column);
// Below native methods don't do unconstrained work, so are FastNative for performance
@FastNative
+ @RavenwoodThrow
private static native void nativeClear(long windowPtr);
@FastNative
+ @RavenwoodRedirect
private static native int nativeGetNumRows(long windowPtr);
@FastNative
+ @RavenwoodRedirect
private static native boolean nativeSetNumColumns(long windowPtr, int columnNum);
@FastNative
+ @RavenwoodRedirect
private static native boolean nativeAllocRow(long windowPtr);
@FastNative
+ @RavenwoodThrow
private static native void nativeFreeLastRow(long windowPtr);
@FastNative
+ @RavenwoodRedirect
private static native int nativeGetType(long windowPtr, int row, int column);
@FastNative
+ @RavenwoodRedirect
private static native long nativeGetLong(long windowPtr, int row, int column);
@FastNative
+ @RavenwoodRedirect
private static native double nativeGetDouble(long windowPtr, int row, int column);
@FastNative
+ @RavenwoodRedirect
private static native boolean nativePutLong(long windowPtr, long value, int row, int column);
@FastNative
+ @RavenwoodRedirect
private static native boolean nativePutDouble(long windowPtr, double value, int row, int column);
@FastNative
+ @RavenwoodThrow
private static native boolean nativePutNull(long windowPtr, int row, int column);
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 10c37301b0b0492929a60fa656dbb32ab5add7de..e0b9f60f812c80b061d9cab3d1ecc48138ed3d3c 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -17,7 +17,9 @@
package android.hardware;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.input.InputSensorInfo;
import android.os.Build;
@@ -1182,6 +1184,8 @@ public final class Sensor {
/** @hide */
@UnsupportedAppUsage
+ @SuppressLint("UnflaggedApi") // Promotion to TestApi
+ @TestApi
public int getHandle() {
return mHandle;
}
diff --git a/core/java/android/hardware/biometrics/AuthenticateOptions.java b/core/java/android/hardware/biometrics/AuthenticateOptions.java
index 77660713275fdb11a8c4c4ff50517d1f7ebea70d..4dc6ea19a35d9ec345dea75825a78c751b2e1ff2 100644
--- a/core/java/android/hardware/biometrics/AuthenticateOptions.java
+++ b/core/java/android/hardware/biometrics/AuthenticateOptions.java
@@ -74,4 +74,7 @@ public interface AuthenticateOptions {
/** The attribution tag, if any. */
@Nullable String getAttributionTag();
+
+ /** If the authentication is requested due to mandatory biometrics being active. */
+ boolean isMandatoryBiometrics();
}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index fc72db3c5791acf209d68ac9f450ca3ea4bd8c8a..6682b367e9779a131396f5c65858dd8cde6d9e06 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -61,7 +61,6 @@ public interface BiometricFingerprintConstants {
BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
BIOMETRIC_ERROR_RE_ENROLL,
- BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
FINGERPRINT_ERROR_UNKNOWN,
FINGERPRINT_ERROR_BAD_CALIBRATION,
BIOMETRIC_ERROR_POWER_PRESSED})
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index 17cd18cc41825963782c1096d75bc08187c80fdf..b195225420b9b42be20b1817f8b9851a450b21d8 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -49,7 +49,7 @@ interface IBiometricAuthenticator {
void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
long requestId, int cookie, boolean allowBackgroundAuthentication,
- boolean isForLegacyFingerprintManager);
+ boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics);
// Starts authentication with the previously prepared client.
void startPreparedClient(int cookie);
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index fbed50ad3c2d1f1a9d54732d494534a1a4cf8269..ac721168a5f3e64eea3c83a97d5ca88d9b7e6af6 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1537,9 +1537,14 @@ public final class CaptureRequest extends CameraMetadata>
* be made, and for firing pre-capture flash pulses to estimate
* scene brightness and required final capture flash power, when
* the flash is enabled.
- * Normally, this entry should be set to START for only a
- * single request, and the application should wait until the
- * sequence completes before starting a new one.
+ * Flash is enabled during precapture sequence when:
+ *
+ * - AE mode is ON_ALWAYS_FLASH
+ * - AE mode is ON_AUTO_FLASH and the scene is deemed too dark without flash, or
+ * - AE mode is ON and flash mode is TORCH or SINGLE
+ *
+ * Normally, this entry should be set to START for only single request, and the
+ * application should wait until the sequence completes before starting a new one.
* When a precapture metering sequence is finished, the camera device
* may lock the auto-exposure routine internally to be able to accurately expose the
* subsequent still capture image ({@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE).
@@ -2705,6 +2710,13 @@ public final class CaptureRequest extends CameraMetadata>
* in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
* If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
* ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.
+ * When AE mode is ON and flash mode is TORCH or SINGLE, the application should make sure
+ * the AE mode, flash mode, and flash strength level remain the same between precapture
+ * trigger request and final capture request. The flash strength level being set during
+ * precapture sequence is used by the camera device as a reference. The actual strength
+ * may be less, and the auto-exposure routine makes sure proper conversions of sensor
+ * exposure time and sensitivities between precapture and final capture for the specified
+ * strength level.
* Range of valid values:
* [1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}] when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
* set to TORCH;
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index d652b4cc42cd12319a82becd323dd6cc8e1af493..34ce92c0f498711b963d326af15664d8b0efbbcd 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -935,9 +935,14 @@ public class CaptureResult extends CameraMetadata> {
* be made, and for firing pre-capture flash pulses to estimate
* scene brightness and required final capture flash power, when
* the flash is enabled.
- * Normally, this entry should be set to START for only a
- * single request, and the application should wait until the
- * sequence completes before starting a new one.
+ * Flash is enabled during precapture sequence when:
+ *
+ * - AE mode is ON_ALWAYS_FLASH
+ * - AE mode is ON_AUTO_FLASH and the scene is deemed too dark without flash, or
+ * - AE mode is ON and flash mode is TORCH or SINGLE
+ *
+ * Normally, this entry should be set to START for only single request, and the
+ * application should wait until the sequence completes before starting a new one.
* When a precapture metering sequence is finished, the camera device
* may lock the auto-exposure routine internally to be able to accurately expose the
* subsequent still capture image ({@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE).
@@ -2821,8 +2826,6 @@ public class CaptureResult extends CameraMetadata> {
* boost when the light level threshold is exceeded.
* This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
* indicate when it is not being applied by returning 'INACTIVE'.
- * This key will be absent from the CaptureResult if AE mode is not set to
- * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.
* The default value will always be 'INACTIVE'.
* Possible values:
*
@@ -2996,6 +2999,13 @@ public class CaptureResult extends CameraMetadata> {
* in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
* If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
* ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.
+ * When AE mode is ON and flash mode is TORCH or SINGLE, the application should make sure
+ * the AE mode, flash mode, and flash strength level remain the same between precapture
+ * trigger request and final capture request. The flash strength level being set during
+ * precapture sequence is used by the camera device as a reference. The actual strength
+ * may be less, and the auto-exposure routine makes sure proper conversions of sensor
+ * exposure time and sensitivities between precapture and final capture for the specified
+ * strength level.
* Range of valid values:
* [1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}] when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
* set to TORCH;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index a60c48e0aa8c963ecfbe46eb2bb4223de462a690..c7dba6c8389532c63e4a226993ce70412ea08eed 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -80,6 +80,7 @@ import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -90,6 +91,17 @@ public class CameraDeviceImpl extends CameraDevice
private final String TAG;
private final boolean DEBUG = false;
+ private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+ private static final ThreadFactory mFactory = Executors.defaultThreadFactory();
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = mFactory.newThread(r);
+ thread.setName("CameraDeviceExecutor");
+ return thread;
+ }
+ };
+
private static final int REQUEST_ID_NONE = -1;
/**
@@ -354,7 +366,11 @@ public class CameraDeviceImpl extends CameraDevice
mCameraId = cameraId;
if (Flags.singleThreadExecutor()) {
mDeviceCallback = new ClientStateCallback(executor, callback);
- mDeviceExecutor = Executors.newSingleThreadExecutor();
+ if (Flags.singleThreadExecutorNaming()) {
+ mDeviceExecutor = Executors.newSingleThreadExecutor(sThreadFactory);
+ } else {
+ mDeviceExecutor = Executors.newSingleThreadExecutor();
+ }
} else {
mDeviceCallback = callback;
mDeviceExecutor = executor;
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 109b0a8be7f86124d9c8951255d8f4b84145ced2..6a96a54d93baabc0cf3f1874a9f1f2da17daed5b 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -158,6 +158,8 @@ public final class BrightnessInfo implements Parcelable {
return "thermal";
case BRIGHTNESS_MAX_REASON_POWER_IC:
return "power IC";
+ case BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE:
+ return "wear bedtime";
}
return "invalid";
}
diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java
index 518f902acdcb584b74535f123d612f9a40c03aa5..8babbfa7169d6c3c3162425303a27268a00c61f9 100644
--- a/core/java/android/hardware/face/FaceAuthenticateOptions.java
+++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java
@@ -120,6 +120,8 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
}
+ /** If the authentication is requested due to mandatory biometrics being active. */
+ private boolean mIsMandatoryBiometrics;
// Code below generated by codegen v1.0.23.
//
@@ -188,7 +190,8 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
@AuthenticateReason int authenticateReason,
@PowerManager.WakeReason int wakeReason,
@NonNull String opPackageName,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag,
+ boolean isMandatoryBiometrics) {
this.mUserId = userId;
this.mSensorId = sensorId;
this.mDisplayState = displayState;
@@ -229,6 +232,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mOpPackageName);
this.mAttributionTag = attributionTag;
+ this.mIsMandatoryBiometrics = isMandatoryBiometrics;
// onConstructed(); // You can define this method to get a callback
}
@@ -261,7 +265,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
* The reason for this operation when requested by the system (sysui),
* otherwise AUTHENTICATE_REASON_UNKNOWN.
*
- * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
+ * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
* for more details about each reason.
*/
@DataClass.Generated.Member
@@ -298,6 +302,14 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
return mAttributionTag;
}
+ /**
+ * If the authentication is requested due to mandatory biometrics being active.
+ */
+ @DataClass.Generated.Member
+ public boolean isMandatoryBiometrics() {
+ return mIsMandatoryBiometrics;
+ }
+
/**
* The sensor id for this operation.
*/
@@ -332,6 +344,15 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
return this;
}
+ /**
+ * If the authentication is requested due to mandatory biometrics being active.
+ */
+ @DataClass.Generated.Member
+ public @NonNull FaceAuthenticateOptions setIsMandatoryBiometrics( boolean value) {
+ mIsMandatoryBiometrics = value;
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
@@ -351,7 +372,8 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
&& mAuthenticateReason == that.mAuthenticateReason
&& mWakeReason == that.mWakeReason
&& java.util.Objects.equals(mOpPackageName, that.mOpPackageName)
- && java.util.Objects.equals(mAttributionTag, that.mAttributionTag);
+ && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+ && mIsMandatoryBiometrics == that.mIsMandatoryBiometrics;
}
@Override
@@ -368,6 +390,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
_hash = 31 * _hash + mWakeReason;
_hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName);
_hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+ _hash = 31 * _hash + Boolean.hashCode(mIsMandatoryBiometrics);
return _hash;
}
@@ -377,9 +400,10 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
- byte flg = 0;
+ int flg = 0;
+ if (mIsMandatoryBiometrics) flg |= 0x80;
if (mAttributionTag != null) flg |= 0x40;
- dest.writeByte(flg);
+ dest.writeInt(flg);
dest.writeInt(mUserId);
dest.writeInt(mSensorId);
dest.writeInt(mDisplayState);
@@ -400,7 +424,8 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
- byte flg = in.readByte();
+ int flg = in.readInt();
+ boolean isMandatoryBiometrics = (flg & 0x80) != 0;
int userId = in.readInt();
int sensorId = in.readInt();
int displayState = in.readInt();
@@ -449,6 +474,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mOpPackageName);
this.mAttributionTag = attributionTag;
+ this.mIsMandatoryBiometrics = isMandatoryBiometrics;
// onConstructed(); // You can define this method to get a callback
}
@@ -481,6 +507,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
private @PowerManager.WakeReason int mWakeReason;
private @NonNull String mOpPackageName;
private @Nullable String mAttributionTag;
+ private boolean mIsMandatoryBiometrics;
private long mBuilderFieldsSet = 0L;
@@ -524,7 +551,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
* The reason for this operation when requested by the system (sysui),
* otherwise AUTHENTICATE_REASON_UNKNOWN.
*
- * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
+ * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
* for more details about each reason.
*/
@DataClass.Generated.Member
@@ -573,10 +600,21 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
return this;
}
+ /**
+ * If the authentication is requested due to mandatory biometrics being active.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setIsMandatoryBiometrics(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80;
+ mIsMandatoryBiometrics = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull FaceAuthenticateOptions build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x80; // Mark builder used
+ mBuilderFieldsSet |= 0x100; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mUserId = defaultUserId();
@@ -606,12 +644,13 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
mAuthenticateReason,
mWakeReason,
mOpPackageName,
- mAttributionTag);
+ mAttributionTag,
+ mIsMandatoryBiometrics);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x80) != 0) {
+ if ((mBuilderFieldsSet & 0x100) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -619,10 +658,10 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
}
@DataClass.Generated(
- time = 1677119626034L,
+ time = 1723436679828L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/hardware/face/FaceAuthenticateOptions.java",
- inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final int AUTHENTICATE_REASON_UNKNOWN\npublic static final int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static int defaultDisplayState()\nprivate static int defaultAuthenticateReason()\nprivate static int defaultWakeReason()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final int AUTHENTICATE_REASON_UNKNOWN\npublic static final int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate boolean mIsMandatoryBiometrics\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static int defaultDisplayState()\nprivate static int defaultAuthenticateReason()\nprivate static int defaultWakeReason()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
index dc66542f4f374708c83822236fbcf6857f22c1a8..ddf1e5b9dd175c2568bfa51cd8485b2f1ed75e11 100644
--- a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
+++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
@@ -97,6 +97,11 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
return null;
}
+ /**
+ * If the authentication is requested due to mandatory biometrics being active.
+ */
+ private boolean mIsMandatoryBiometrics;
+
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
@@ -118,7 +123,8 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
@AuthenticateOptions.DisplayState int displayState,
@NonNull String opPackageName,
@Nullable String attributionTag,
- @Nullable AuthenticateReason.Vendor vendorReason) {
+ @Nullable AuthenticateReason.Vendor vendorReason,
+ boolean isMandatoryBiometrics) {
this.mUserId = userId;
this.mSensorId = sensorId;
this.mIgnoreEnrollmentState = ignoreEnrollmentState;
@@ -130,6 +136,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
NonNull.class, null, mOpPackageName);
this.mAttributionTag = attributionTag;
this.mVendorReason = vendorReason;
+ this.mIsMandatoryBiometrics = isMandatoryBiometrics;
// onConstructed(); // You can define this method to get a callback
}
@@ -198,6 +205,14 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
return mVendorReason;
}
+ /**
+ * If the authentication is requested due to mandatory biometrics being active.
+ */
+ @DataClass.Generated.Member
+ public boolean isMandatoryBiometrics() {
+ return mIsMandatoryBiometrics;
+ }
+
/**
* The sensor id for this operation.
*/
@@ -244,6 +259,15 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
return this;
}
+ /**
+ * If the authentication is requested due to mandatory biometrics being active.
+ */
+ @DataClass.Generated.Member
+ public @NonNull FingerprintAuthenticateOptions setIsMandatoryBiometrics( boolean value) {
+ mIsMandatoryBiometrics = value;
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
@@ -263,7 +287,8 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
&& mDisplayState == that.mDisplayState
&& java.util.Objects.equals(mOpPackageName, that.mOpPackageName)
&& java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
- && java.util.Objects.equals(mVendorReason, that.mVendorReason);
+ && java.util.Objects.equals(mVendorReason, that.mVendorReason)
+ && mIsMandatoryBiometrics == that.mIsMandatoryBiometrics;
}
@Override
@@ -280,6 +305,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
_hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName);
_hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
_hash = 31 * _hash + java.util.Objects.hashCode(mVendorReason);
+ _hash = 31 * _hash + Boolean.hashCode(mIsMandatoryBiometrics);
return _hash;
}
@@ -289,11 +315,12 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
- byte flg = 0;
+ int flg = 0;
if (mIgnoreEnrollmentState) flg |= 0x4;
+ if (mIsMandatoryBiometrics) flg |= 0x80;
if (mAttributionTag != null) flg |= 0x20;
if (mVendorReason != null) flg |= 0x40;
- dest.writeByte(flg);
+ dest.writeInt(flg);
dest.writeInt(mUserId);
dest.writeInt(mSensorId);
dest.writeInt(mDisplayState);
@@ -313,8 +340,9 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
- byte flg = in.readByte();
+ int flg = in.readInt();
boolean ignoreEnrollmentState = (flg & 0x4) != 0;
+ boolean isMandatoryBiometrics = (flg & 0x80) != 0;
int userId = in.readInt();
int sensorId = in.readInt();
int displayState = in.readInt();
@@ -333,6 +361,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
NonNull.class, null, mOpPackageName);
this.mAttributionTag = attributionTag;
this.mVendorReason = vendorReason;
+ this.mIsMandatoryBiometrics = isMandatoryBiometrics;
// onConstructed(); // You can define this method to get a callback
}
@@ -365,6 +394,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
private @NonNull String mOpPackageName;
private @Nullable String mAttributionTag;
private @Nullable AuthenticateReason.Vendor mVendorReason;
+ private boolean mIsMandatoryBiometrics;
private long mBuilderFieldsSet = 0L;
@@ -456,10 +486,21 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
return this;
}
+ /**
+ * If the authentication is requested due to mandatory biometrics being active.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setIsMandatoryBiometrics(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80;
+ mIsMandatoryBiometrics = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull FingerprintAuthenticateOptions build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x80; // Mark builder used
+ mBuilderFieldsSet |= 0x100; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mUserId = defaultUserId();
@@ -489,12 +530,13 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
mDisplayState,
mOpPackageName,
mAttributionTag,
- mVendorReason);
+ mVendorReason,
+ mIsMandatoryBiometrics);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x80) != 0) {
+ if ((mBuilderFieldsSet & 0x100) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -502,10 +544,10 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
}
@DataClass.Generated(
- time = 1689703591032L,
+ time = 1723436831455L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java",
- inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nprivate static android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate boolean mIsMandatoryBiometrics\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nprivate static android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 83f268517dab3549fb96ecc7820bf0aefda435e1..2d96bbaae901f6bf4204a7caa769ad74335c8815 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -241,12 +241,12 @@ interface IInputManager {
KeyGlyphMap getKeyGlyphMap(int deviceId);
- @EnforcePermission("MANAGE_KEY_GESTURES")
+ @PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void registerKeyGestureEventListener(IKeyGestureEventListener listener);
- @EnforcePermission("MANAGE_KEY_GESTURES")
+ @PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void unregisterKeyGestureEventListener(IKeyGestureEventListener listener);
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 03cf7c5f926ced7d668e1912835724bd36ede12a..2a362381a0035c4b45c60bb3dcb2651fa05bae0f 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -567,7 +567,7 @@ public final class InputManagerGlobal {
Objects.requireNonNull(listener, "listener must not be null");
synchronized (mOnTabletModeChangedListeners) {
- if (mOnTabletModeChangedListeners == null) {
+ if (mOnTabletModeChangedListeners.isEmpty()) {
initializeTabletModeListenerLocked();
}
int idx = findOnTabletModeChangedListenerLocked(listener);
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index c5d0caf228d20e3af43a3d7427f245d4a5c7bf77..8592dedbb2bb7166df672c102e86d23477c9478d 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -20,10 +20,12 @@ import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FL
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS;
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG;
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG;
+import static com.android.hardware.input.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11ySlowKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
+import static com.android.hardware.input.Flags.keyboardRepeatKeys;
import static com.android.hardware.input.Flags.touchpadTapDragging;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.input.flags.Flags.enableInputFilterRustImpl;
@@ -40,6 +42,7 @@ import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import android.sysprop.InputProperties;
+import android.view.ViewConfiguration;
/**
* InputSettings encapsulates reading and writing settings related to input
@@ -90,6 +93,30 @@ public class InputSettings {
*/
public static final int DEFAULT_STYLUS_POINTER_ICON_ENABLED = 1;
+ /**
+ * The minimum allowed repeat keys timeout before starting key repeats.
+ * @hide
+ */
+ public static final int MIN_KEY_REPEAT_TIMEOUT_MILLIS = 150;
+
+ /**
+ * The maximum allowed repeat keys timeout before starting key repeats.
+ * @hide
+ */
+ public static final int MAX_KEY_REPEAT_TIMEOUT_MILLIS = 2000;
+
+ /**
+ * The minimum allowed repeat keys delay between successive key repeats.
+ * @hide
+ */
+ public static final int MIN_KEY_REPEAT_DELAY_MILLIS = 20;
+
+ /**
+ * The maximum allowed repeat keys delay between successive key repeats.
+ * @hide
+ */
+ public static final int MAX_KEY_REPEAT_DELAY_MILLIS = 2000;
+
private InputSettings() {
}
@@ -767,4 +794,141 @@ public class InputSettings {
Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
}
+
+ /**
+ * Whether "Repeat keys" feature flag is enabled.
+ *
+ *
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ *
+ *
+ * @hide
+ */
+ public static boolean isRepeatKeysFeatureFlagEnabled() {
+ return keyboardRepeatKeys();
+ }
+
+ /**
+ * Get Accessibility repeat keys timeout duration in milliseconds.
+ * The default key repeat timeout is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_TIMEOUT_MS}.
+ *
+ * @param context The application context
+ * @return The time duration for which a key should be pressed after
+ * which the pressed key will be repeated. The timeout must be between
+ * {@link #MIN_KEY_REPEAT_TIMEOUT_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_TIMEOUT_MILLIS}
+ *
+ *
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ *
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ public static int getAccessibilityRepeatKeysTimeout(@NonNull Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS, ViewConfiguration.getKeyRepeatTimeout(),
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Get Accessibility repeat keys delay rate in milliseconds.
+ * The default key repeat delay is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_DELAY_MS}.
+ *
+ * @param context The application context
+ * @return Time duration between successive key repeats when a key is
+ * pressed down. The delay duration must be between
+ * {@link #MIN_KEY_REPEAT_DELAY_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_DELAY_MILLIS}
+ *
+ *
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ *
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ public static int getAccessibilityRepeatKeysDelay(@NonNull Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(),
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Set Accessibility repeat keys timeout duration in milliseconds.
+ *
+ * @param timeoutTimeMillis time duration for which a key should be pressed after which the
+ * pressed key will be repeated. The timeout must be between
+ * {@link #MIN_KEY_REPEAT_TIMEOUT_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_TIMEOUT_MILLIS}
+ *
+ *
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ *
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setAccessibilityRepeatKeysTimeout(@NonNull Context context,
+ int timeoutTimeMillis) {
+ if (timeoutTimeMillis < MIN_KEY_REPEAT_TIMEOUT_MILLIS
+ || timeoutTimeMillis > MAX_KEY_REPEAT_TIMEOUT_MILLIS) {
+ throw new IllegalArgumentException(
+ "Provided repeat keys timeout should be in range ("
+ + MIN_KEY_REPEAT_TIMEOUT_MILLIS + ","
+ + MAX_KEY_REPEAT_TIMEOUT_MILLIS + ")");
+ }
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS, timeoutTimeMillis,
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Set Accessibility repeat key delay duration in milliseconds.
+ *
+ * @param delayTimeMillis Time duration between successive key repeats when a key is
+ * pressed down. The delay duration must be between
+ * {@link #MIN_KEY_REPEAT_DELAY_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_DELAY_MILLIS}
+ *
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ *
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setAccessibilityRepeatKeysDelay(@NonNull Context context,
+ int delayTimeMillis) {
+ if (delayTimeMillis < MIN_KEY_REPEAT_DELAY_MILLIS
+ || delayTimeMillis > MAX_KEY_REPEAT_DELAY_MILLIS) {
+ throw new IllegalArgumentException(
+ "Provided repeat keys delay should be in range ("
+ + MIN_KEY_REPEAT_DELAY_MILLIS + ","
+ + MAX_KEY_REPEAT_DELAY_MILLIS + ")");
+ }
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_DELAY_MS, delayTimeMillis,
+ UserHandle.USER_CURRENT);
+ }
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 83c4de31824db96e238e14bdfec03a18c0a5f9b0..fcd6c31d7d18924da6e6265b416e6b4fafe1f61e 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -99,3 +99,21 @@ flag {
description: "Refactor ModifierShortcutManager internal representation of shortcuts."
bug: "358603902"
}
+
+flag {
+ namespace: "input_native"
+ name: "manage_key_gestures"
+ description: "Manage key gestures through Input APIs"
+ is_exported: true
+ bug: "358569822"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "keyboard_repeat_keys"
+ namespace: "input_native"
+ description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
+ bug: "336585002"
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 3a589930feb496e5fd45a10618fe312ed6075053..218b02315fc838092e79be8bb0b49842edb7655a 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -256,6 +256,10 @@ public final class ContextHubManager {
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int[] getContextHubHandles() {
+ if (Flags.removeOldContextHubApis()) {
+ return null;
+ }
+
try {
return mService.getContextHubHandles();
} catch (RemoteException e) {
@@ -277,6 +281,10 @@ public final class ContextHubManager {
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public ContextHubInfo getContextHubInfo(int hubHandle) {
+ if (Flags.removeOldContextHubApis()) {
+ return null;
+ }
+
try {
return mService.getContextHubInfo(hubHandle);
} catch (RemoteException e) {
@@ -308,6 +316,10 @@ public final class ContextHubManager {
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int loadNanoApp(int hubHandle, @NonNull NanoApp app) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
try {
return mService.loadNanoApp(hubHandle, app);
} catch (RemoteException e) {
@@ -335,6 +347,10 @@ public final class ContextHubManager {
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int unloadNanoApp(int nanoAppHandle) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
try {
return mService.unloadNanoApp(nanoAppHandle);
} catch (RemoteException e) {
@@ -375,6 +391,10 @@ public final class ContextHubManager {
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
+ if (Flags.removeOldContextHubApis()) {
+ return null;
+ }
+
try {
return mService.getNanoAppInstanceInfo(nanoAppHandle);
} catch (RemoteException e) {
@@ -398,6 +418,10 @@ public final class ContextHubManager {
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) {
+ if (Flags.removeOldContextHubApis()) {
+ return null;
+ }
+
try {
return mService.findNanoAppOnHub(hubHandle, filter);
} catch (RemoteException e) {
@@ -433,6 +457,10 @@ public final class ContextHubManager {
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
try {
return mService.sendMessage(hubHandle, nanoAppHandle, message);
} catch (RemoteException e) {
@@ -648,6 +676,10 @@ public final class ContextHubManager {
@Deprecated
@SuppressLint("RequiresPermission")
public int registerCallback(@NonNull Callback callback) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
return registerCallback(callback, null);
}
@@ -657,6 +689,10 @@ public final class ContextHubManager {
*/
@Deprecated
public int registerCallback(ICallback callback) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
if (mLocalCallback != null) {
Log.w(TAG, "Max number of local callbacks reached!");
return -1;
@@ -682,6 +718,10 @@ public final class ContextHubManager {
@Deprecated
@SuppressLint("RequiresPermission")
public int registerCallback(Callback callback, Handler handler) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
synchronized(this) {
if (mCallback != null) {
Log.w(TAG, "Max number of callbacks reached!");
@@ -1041,16 +1081,20 @@ public final class ContextHubManager {
@SuppressLint("RequiresPermission")
@Deprecated
public int unregisterCallback(@NonNull Callback callback) {
- synchronized(this) {
- if (callback != mCallback) {
- Log.w(TAG, "Cannot recognize callback!");
- return -1;
- }
-
- mCallback = null;
- mCallbackHandler = null;
- }
- return 0;
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
+ synchronized (this) {
+ if (callback != mCallback) {
+ Log.w(TAG, "Cannot recognize callback!");
+ return -1;
+ }
+
+ mCallback = null;
+ mCallbackHandler = null;
+ }
+ return 0;
}
/**
@@ -1059,6 +1103,10 @@ public final class ContextHubManager {
*/
@Deprecated
public synchronized int unregisterCallback(ICallback callback) {
+ if (Flags.removeOldContextHubApis()) {
+ return -1;
+ }
+
if (callback != mLocalCallback) {
Log.w(TAG, "Cannot recognize local callback!");
return -1;
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index bfff4dbdd627afdedba0bab7bc6b2b7ec588d294..9f3e3ad8c01e869866eaf9e800a41b40de1b4096 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -29,6 +29,7 @@ import static android.system.OsConstants.EPIPE;
import static java.util.Objects.requireNonNull;
import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -874,10 +875,9 @@ public class SoundTrigger {
/*****************************************************************************
* A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
* patterns.
- *
- * @hide
****************************************************************************/
- public static class GenericSoundModel extends SoundModel implements Parcelable {
+ @FlaggedApi(android.media.soundtrigger.Flags.FLAG_GENERIC_MODEL_API)
+ public static final class GenericSoundModel extends SoundModel implements Parcelable {
public static final @android.annotation.NonNull Parcelable.Creator CREATOR
= new Parcelable.Creator() {
@@ -890,11 +890,26 @@ public class SoundTrigger {
}
};
+ /**
+ * Constructor for {@link GenericSoundModel} with version.
+ *
+ * @param uuid Unique identifier for this sound model.
+ * @param vendorUuid Unique vendor identifier for this sound model.
+ * @param data Opaque data for this sound model.
+ * @param version Vendor-specific version number of this sound model.
+ */
public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
@Nullable byte[] data, int version) {
super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version);
}
+ /**
+ * Constructor for {@link GenericSoundModel} without version. The version is set to -1.
+ *
+ * @param uuid Unique identifier for this sound model.
+ * @param vendorUuid Unique vendor identifier for this sound model.
+ * @param data Opaque data for this sound model.
+ */
@UnsupportedAppUsage
public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
@Nullable byte[] data) {
@@ -919,7 +934,7 @@ public class SoundTrigger {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(getUuid().toString());
if (getVendorUuid() == null) {
dest.writeInt(-1);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index ff737a4f9fb889c7888304054b19533850cc04c5..49e23584cf4f95628a61d6892ad1adbd21cdab79 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -664,9 +664,14 @@ public class InputMethodService extends AbstractInputMethodService {
int mStatusIcon;
+ /** Latest reported value of back disposition mode. */
@BackDispositionMode
int mBackDisposition;
+ /** Latest reported value of IME window visibility state. */
+ @ImeWindowVisibility
+ private int mImeWindowVisibility;
+
private Object mLock = new Object();
@GuardedBy("mLock")
private boolean mNotifyUserActionSent;
@@ -1047,7 +1052,7 @@ public class InputMethodService extends AbstractInputMethodService {
ImeTracker.forLogging().onFailed(statsToken,
ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
}
- setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ setImeWindowVisibility(computeImeWindowVis());
final boolean isVisible = isInputViewShown();
final boolean visibilityChanged = isVisible != wasVisible;
@@ -1357,9 +1362,22 @@ public class InputMethodService extends AbstractInputMethodService {
mImeSurfaceRemoverRunnable = null;
}
- private void setImeWindowStatus(@ImeWindowVisibility int visibilityFlags,
+ /**
+ * Sets the IME window visibility state.
+ *
+ * @param vis the IME window visibility state to be set.
+ */
+ private void setImeWindowVisibility(@ImeWindowVisibility int vis) {
+ if (vis == mImeWindowVisibility) {
+ return;
+ }
+ mImeWindowVisibility = vis;
+ setImeWindowStatus(mImeWindowVisibility, mBackDisposition);
+ }
+
+ private void setImeWindowStatus(@ImeWindowVisibility int vis,
@BackDispositionMode int backDisposition) {
- mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition);
+ mPrivOps.setImeWindowStatusAsync(vis, backDisposition);
}
/** Set region of the keyboard to be avoided from back gesture */
@@ -1986,7 +2004,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
- setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+ setImeWindowVisibility(IME_ACTIVE | (showing ? IME_VISIBLE : 0));
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -2053,7 +2071,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
mBackDisposition = disposition;
- setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ setImeWindowStatus(mImeWindowVisibility, mBackDisposition);
}
/**
@@ -3132,14 +3150,8 @@ public class InputMethodService extends AbstractInputMethodService {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
mDecorViewWasVisible = mDecorViewVisible;
mInShowWindow = true;
- final int previousImeWindowStatus =
- (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
- ? (!mWindowVisible ? -1 : IME_VISIBLE) : 0);
startViews(prepareWindow(showInput));
- final int nextImeWindowStatus = mapToImeWindowStatus();
- if (previousImeWindowStatus != nextImeWindowStatus) {
- setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
- }
+ setImeWindowVisibility(computeImeWindowVis());
mNavigationBarController.onWindowShown();
// compute visibility
@@ -3317,7 +3329,7 @@ public class InputMethodService extends AbstractInputMethodService {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW);
ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
null /* icProto */);
- setImeWindowStatus(0 /* visibilityFlags */, mBackDisposition);
+ setImeWindowVisibility(0 /* vis */);
if (android.view.inputmethod.Flags.refactorInsetsController()) {
// The ImeInsetsSourceProvider need the statsToken when dispatching the control. We
// send the token here, so that another request in the provider can be cancelled.
@@ -4492,10 +4504,10 @@ public class InputMethodService extends AbstractInputMethodService {
};
}
+ /** Computes the IME window visibility state. */
@ImeWindowVisibility
- private int mapToImeWindowStatus() {
- return IME_ACTIVE
- | (isInputViewShown() ? IME_VISIBLE : 0);
+ private int computeImeWindowVis() {
+ return IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
}
/**
diff --git a/core/java/android/os/ArtModuleServiceManager.java b/core/java/android/os/ArtModuleServiceManager.java
index e0b631d69ca871b5097bf1f03896d8cd26db4ab1..995094bb1318d408a223953ec86395f8c203b6ab 100644
--- a/core/java/android/os/ArtModuleServiceManager.java
+++ b/core/java/android/os/ArtModuleServiceManager.java
@@ -37,10 +37,12 @@ public class ArtModuleServiceManager {
/** A class that exposes the method to obtain each system service. */
public static final class ServiceRegisterer {
@NonNull private final String mServiceName;
+ private final boolean mRetry;
/** @hide */
- public ServiceRegisterer(@NonNull String serviceName) {
+ public ServiceRegisterer(@NonNull String serviceName, boolean retry) {
mServiceName = serviceName;
+ mRetry = retry;
}
/**
@@ -53,27 +55,47 @@ public class ArtModuleServiceManager {
*/
@Nullable
public IBinder waitForService() {
- return ServiceManager.waitForService(mServiceName);
+ if (mRetry) {
+ return ServiceManager.waitForService(mServiceName);
+ }
+ IBinder binder = ServiceManager.getService(mServiceName);
+ for (int remainingTimeMs = 5000; binder == null && remainingTimeMs > 0;
+ remainingTimeMs -= 100) {
+ // There can be a race:
+ // 1. Client A invokes "ctl.start", which starts the service.
+ // 2. Client A gets a service handle from `ServiceManager.getService`.
+ // 3. Client B invokes "ctl.start", which does nothing because the service is
+ // already running.
+ // 4. Client A drops the service handle. The service is notified that there is no
+ // more client at that point, so it shuts down itself.
+ // 5. Client B cannot get a service handle from `ServiceManager.getService` because
+ // the service is shut down.
+ // To address this problem, we invoke "ctl.start" repeatedly.
+ SystemProperties.set("ctl.start", mServiceName);
+ SystemClock.sleep(100);
+ binder = ServiceManager.getService(mServiceName);
+ }
+ return binder;
}
}
/** Returns {@link ServiceRegisterer} for the "artd" service. */
@NonNull
public ServiceRegisterer getArtdServiceRegisterer() {
- return new ServiceRegisterer("artd");
+ return new ServiceRegisterer("artd", true /* retry */);
}
/** Returns {@link ServiceRegisterer} for the "artd_pre_reboot" service. */
@NonNull
@FlaggedApi(Flags.FLAG_USE_ART_SERVICE_V2)
public ServiceRegisterer getArtdPreRebootServiceRegisterer() {
- return new ServiceRegisterer("artd_pre_reboot");
+ return new ServiceRegisterer("artd_pre_reboot", false /* retry */);
}
/** Returns {@link ServiceRegisterer} for the "dexopt_chroot_setup" service. */
@NonNull
@FlaggedApi(Flags.FLAG_USE_ART_SERVICE_V2)
public ServiceRegisterer getDexoptChrootSetupServiceRegisterer() {
- return new ServiceRegisterer("dexopt_chroot_setup");
+ return new ServiceRegisterer("dexopt_chroot_setup", true /* retry */);
}
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b7556dfb51af5a3f83c2837bceaa8d4e23ef687e..4bc3dbedeb9448003837c502c0de161e7511fe16 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -708,9 +708,16 @@ public class Binder implements IBinder {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
public final native void markVintfStability();
+ /** @hide */
+ private void markVintfStability$ravenwood() {
+ // This is not useful for Ravenwood which uses local binder.
+ // TODO(b/361785059): Use real native libbinder.
+ }
+
/**
* Use a VINTF-stability binder w/o VINTF requirements. Should be called
* on a binder before it is sent out of process.
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 1100731702a299b7a21cb1de41fbe06ae1c466d8..c22f46cdc2b5bdc1e53d70c2b9f25036161abcbe 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -645,6 +645,37 @@ public final class BinderProxy implements IBinder {
private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
+ /**
+ * This list is to hold strong reference to the frozen state callbacks. The callbacks are only
+ * weakly referenced by JNI so the strong references here are needed to keep the callbacks
+ * around until the proxy is GC'ed.
+ */
+ private List mFrozenStateChangeCallbacks =
+ Collections.synchronizedList(new ArrayList<>());
+
+ /**
+ * See {@link IBinder#addFrozenStateChangeCallback(IFrozenStateChangeCallback)}
+ */
+ public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ addFrozenStateChangeCallbackNative(callback);
+ mFrozenStateChangeCallbacks.add(callback);
+ }
+
+ /**
+ * See {@link IBinder#removeFrozenStateChangeCallback}
+ */
+ public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+ mFrozenStateChangeCallbacks.remove(callback);
+ return removeFrozenStateChangeCallbackNative(callback);
+ }
+
+ private native void addFrozenStateChangeCallbackNative(IFrozenStateChangeCallback callback)
+ throws RemoteException;
+
+ private native boolean removeFrozenStateChangeCallbackNative(
+ IFrozenStateChangeCallback callback);
+
/**
* Perform a dump on the remote object
*
@@ -730,6 +761,17 @@ public final class BinderProxy implements IBinder {
}
}
+ private static void invokeFrozenStateChangeCallback(
+ IFrozenStateChangeCallback callback, IBinder binderProxy, int stateIndex) {
+ try {
+ callback.onFrozenStateChanged(binderProxy,
+ IFrozenStateChangeCallback.State.values()[stateIndex]);
+ } catch (RuntimeException exc) {
+ Log.w("BinderNative", "Uncaught exception from frozen state change callback",
+ exc);
+ }
+ }
+
/**
* C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
* native IBinder object, and a DeathRecipientList.
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index da2eec9cbb2804dbc9452d2a115a5917d71880bc..b2d926044869ee4c0cdd965e0f4a3855eb85776b 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -19,9 +19,9 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -51,9 +51,8 @@ import java.util.concurrent.locks.ReentrantLock;
* You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
-@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "ConcurrentMessageQueue";
private static final boolean DEBUG = false;
@@ -345,11 +344,17 @@ public final class MessageQueue {
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private final AtomicInteger mNextBarrierToken = new AtomicInteger(1);
+ @RavenwoodRedirect
private static native long nativeInit();
+ @RavenwoodRedirect
private static native void nativeDestroy(long ptr);
+ @RavenwoodRedirect
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+ @RavenwoodRedirect
private static native void nativeWake(long ptr);
+ @RavenwoodRedirect
private static native boolean nativeIsPolling(long ptr);
+ @RavenwoodRedirect
private static native void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 92b630f3063aeec00a41c901ea9ae2310c0ba78c..80f39bfbdc21786723723182fbb9edaa42b2a7c9 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -46,13 +46,13 @@ import java.lang.reflect.Modifier;
* a {@link Message} object containing a bundle of data that will be
* processed by the Handler's {@link #handleMessage} method (requiring that
* you implement a subclass of Handler).
- *
+ *
*
When posting or sending to a Handler, you can either
* allow the item to be processed as soon as the message queue is ready
* to do so, or specify a delay before it gets processed or absolute time for
* it to be processed. The latter two allow you to implement timeouts,
* ticks, and other timing-based behavior.
- *
+ *
*
When a
* process is created for your application, its main thread is dedicated to
* running a message queue that takes care of managing the top-level
@@ -85,13 +85,13 @@ public class Handler {
*/
boolean handleMessage(@NonNull Message msg);
}
-
+
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
-
+
/**
* Handle system messages here.
*/
@@ -343,8 +343,8 @@ public class Handler {
* The default implementation will either return the class name of the
* message callback if any, or the hexadecimal representation of the
* message "what" field.
- *
- * @param message The message whose name is being queried
+ *
+ * @param message The message whose name is being queried
*/
@NonNull
public String getMessageName(@NonNull Message message) {
@@ -367,7 +367,7 @@ public class Handler {
/**
* Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message.
- *
+ *
* @param what Value to assign to the returned Message.what field.
* @return A Message from the global message pool.
*/
@@ -376,12 +376,12 @@ public class Handler {
{
return Message.obtain(this, what);
}
-
+
/**
- *
- * Same as {@link #obtainMessage()}, except that it also sets the what and obj members
+ *
+ * Same as {@link #obtainMessage()}, except that it also sets the what and obj members
* of the returned Message.
- *
+ *
* @param what Value to assign to the returned Message.what field.
* @param obj Value to assign to the returned Message.obj field.
* @return A Message from the global message pool.
@@ -392,7 +392,7 @@ public class Handler {
}
/**
- *
+ *
* Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned
* Message.
* @param what Value to assign to the returned Message.what field.
@@ -405,10 +405,10 @@ public class Handler {
{
return Message.obtain(this, what, arg1, arg2);
}
-
+
/**
- *
- * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the
+ *
+ * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the
* returned Message.
* @param what Value to assign to the returned Message.what field.
* @param arg1 Value to assign to the returned Message.arg1 field.
@@ -423,19 +423,19 @@ public class Handler {
/**
* Causes the Runnable r to be added to the message queue.
- * The runnable will be run on the thread to which this handler is
- * attached.
- *
+ * The runnable will be run on the thread to which this handler is
+ * attached.
+ *
* @param r The Runnable that will be executed.
- *
- * @return Returns true if the Runnable was successfully placed in to the
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
-
+
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by uptimeMillis.
@@ -446,8 +446,8 @@ public class Handler {
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the Runnable was successfully placed in to the
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
@@ -457,7 +457,7 @@ public class Handler {
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
-
+
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by uptimeMillis.
@@ -470,21 +470,21 @@ public class Handler {
* {@link #removeCallbacksAndMessages}.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the Runnable was successfully placed in to the
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
- *
+ *
* @see android.os.SystemClock#uptimeMillis
*/
public final boolean postAtTime(
@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
-
+
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses.
@@ -492,12 +492,12 @@ public class Handler {
* is attached.
* The time-base is {@link android.os.SystemClock#uptimeMillis}.
* Time spent in deep sleep will add an additional delay to execution.
- *
+ *
* @param r The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* will be executed.
- *
- * @return Returns true if the Runnable was successfully placed in to the
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed --
@@ -507,7 +507,7 @@ public class Handler {
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
-
+
/** @hide */
public final boolean postDelayed(Runnable r, int what, long delayMillis) {
return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
@@ -547,10 +547,10 @@ public class Handler {
* This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.
- *
+ *
* @param r The Runnable that will be executed.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -635,8 +635,8 @@ public class Handler {
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -646,8 +646,8 @@ public class Handler {
/**
* Sends a Message containing only the what value.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -659,9 +659,9 @@ public class Handler {
/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
- * @see #sendMessageDelayed(android.os.Message, long)
- *
- * @return Returns true if the message was successfully placed in to the
+ * @see #sendMessageDelayed(android.os.Message, long)
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -672,11 +672,11 @@ public class Handler {
}
/**
- * Sends a Message containing only the what value, to be delivered
+ * Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(android.os.Message, long)
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -691,8 +691,8 @@ public class Handler {
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
@@ -713,12 +713,12 @@ public class Handler {
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
- *
+ *
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
@@ -743,8 +743,8 @@ public class Handler {
* This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -798,6 +798,12 @@ public class Handler {
/**
* Remove any pending posts of messages with code 'what' that are in the
* message queue.
+ *
+ * Note that `Message#what` is 0 unless otherwise set.
+ * When calling `postMessage(Runnable)` or `postAtTime(Runnable, long)`,
+ * the `Runnable` is internally wrapped with a `Message` whose `what` is 0.
+ * Calling `removeMessages(0)` will remove all messages without a `what`,
+ * including posted `Runnable`s.
*/
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
@@ -889,7 +895,7 @@ public class Handler {
}
// if we can get rid of this method, the handler need not remember its loop
- // we could instead export a getMessageQueue() method...
+ // we could instead export a getMessageQueue() method...
@NonNull
public final Looper getLooper() {
return mLooper;
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 50242bad191b34144a7ed96f0ea1282d2d4db80d..8185e8e542e1ca20df84e46cc4e0e54525a73a08 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -376,4 +376,53 @@ public interface IBinder {
* return value instead.
*/
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
+
+ /** @hide */
+ interface IFrozenStateChangeCallback {
+ enum State {FROZEN, UNFROZEN};
+
+ /**
+ * Interface for receiving a callback when the process hosting an IBinder
+ * has changed its frozen state.
+ * @param who The IBinder whose hosting process has changed state.
+ * @param state The latest state.
+ */
+ void onFrozenStateChanged(@NonNull IBinder who, State state);
+ }
+
+ /**
+ * {@link addFrozenStateChangeCallback} provides a callback mechanism to notify about process
+ * frozen/unfrozen events. Upon registration and any subsequent state changes, the callback is
+ * invoked with the latest process frozen state.
+ *
+ *
If the listener process (the one using this API) is itself frozen, state change events
+ * might be combined into a single one with the latest frozen state. This single event would
+ * then be delivered when the listener process becomes unfrozen. Similarly, if an event happens
+ * before the previous event is consumed, they might be combined. This means the callback might
+ * not be called for every single state change, so don't rely on this API to count how many
+ * times the state has changed.
+ *
+ * The callback is automatically removed when all references to the binder proxy are
+ * dropped.
+ *
+ * You will only receive state change notifications for remote binders, as local binders by
+ * definition can't be frozen without you being frozen too.
+ *
+ * @throws {@link UnsupportedOperationException} if the kernel binder driver does not support
+ * this feature.
+ * @hide
+ */
+ default void addFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Unregister a {@link IFrozenStateChangeCallback}. The callback will no longer be invoked when
+ * the hosting process changes its frozen state.
+ * @hide
+ */
+ default boolean removeFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index 6b9b3496d1c0861962a49b273b2c58badbda9afa..4474e7e91fdc896b998ab145814f9e75f0d3ff58 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -20,9 +20,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Handler;
-import android.os.Process;
-import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -42,9 +42,8 @@ import java.util.concurrent.atomic.AtomicLong;
*
You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
-@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "MessageQueue";
private static final boolean DEBUG = false;
@@ -79,12 +78,18 @@ public final class MessageQueue {
@UnsupportedAppUsage
private int mNextBarrierToken;
+ @RavenwoodRedirect
private native static long nativeInit();
+ @RavenwoodRedirect
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
+ @RavenwoodRedirect
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+ @RavenwoodRedirect
private native static void nativeWake(long ptr);
+ @RavenwoodRedirect
private native static boolean nativeIsPolling(long ptr);
+ @RavenwoodRedirect
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
diff --git a/core/java/android/os/LockedMessageQueue/MessageQueue.java b/core/java/android/os/LockedMessageQueue/MessageQueue.java
index b24e14b0419e62ab1e7a9f2b1ec52fae177337d1..f1affce58a5c17d43aa81741afa3f996b3f1bfde 100644
--- a/core/java/android/os/LockedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LockedMessageQueue/MessageQueue.java
@@ -20,8 +20,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Handler;
-import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -44,9 +45,8 @@ import java.util.concurrent.atomic.AtomicLong;
*
You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
-@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "LockedMessageQueue";
private static final boolean DEBUG = false;
@@ -389,12 +389,18 @@ public final class MessageQueue {
@UnsupportedAppUsage
private int mNextBarrierToken;
+ @RavenwoodRedirect
private native static long nativeInit();
+ @RavenwoodRedirect
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
+ @RavenwoodRedirect
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+ @RavenwoodRedirect
private native static void nativeWake(long ptr);
+ @RavenwoodRedirect
private native static boolean nativeIsPolling(long ptr);
+ @RavenwoodRedirect
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index a1db9be0b693ea89f191b54d661dc33558a2e7ba..702fdc2bbaa6219417d02dc6c8fa32197e7689af 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -41,6 +41,9 @@ public final class Message implements Parcelable {
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
+ *
+ * If not specified, this value is 0.
+ * Use values other than 0 to indicate custom message codes.
*/
public int what;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 47096dbbac614a380609ed8c99710b1dc147ff2a..2ac2ae916e5893818dd4d59ba8c9656be2e809ba 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -28,7 +28,8 @@ import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.ravenwood.annotation.RavenwoodReplace;
import android.ravenwood.annotation.RavenwoodThrow;
import android.text.TextUtils;
@@ -233,8 +234,7 @@ import java.util.function.IntFunction;
* {@link #readSparseArray(ClassLoader, Class)}.
*/
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.Parcel_host")
+@RavenwoodRedirectionClass("Parcel_host")
public final class Parcel {
private static final boolean DEBUG_RECYCLE = false;
@@ -387,6 +387,7 @@ public final class Parcel {
private static final int SIZE_COMPLEX_TYPE = 1;
@CriticalNative
+ @RavenwoodRedirect
private static native void nativeMarkSensitive(long nativePtr);
@FastNative
@RavenwoodThrow
@@ -395,86 +396,126 @@ public final class Parcel {
@RavenwoodThrow
private static native boolean nativeIsForRpc(long nativePtr);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeDataSize(long nativePtr);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeDataAvail(long nativePtr);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeDataPosition(long nativePtr);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeDataCapacity(long nativePtr);
@FastNative
+ @RavenwoodRedirect
private static native void nativeSetDataSize(long nativePtr, int size);
@CriticalNative
+ @RavenwoodRedirect
private static native void nativeSetDataPosition(long nativePtr, int pos);
@FastNative
+ @RavenwoodRedirect
private static native void nativeSetDataCapacity(long nativePtr, int size);
@CriticalNative
+ @RavenwoodRedirect
private static native boolean nativePushAllowFds(long nativePtr, boolean allowFds);
@CriticalNative
+ @RavenwoodRedirect
private static native void nativeRestoreAllowFds(long nativePtr, boolean lastValue);
+ @RavenwoodRedirect
private static native void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len);
+ @RavenwoodRedirect
private static native void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeWriteInt(long nativePtr, int val);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeWriteLong(long nativePtr, long val);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeWriteFloat(long nativePtr, float val);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeWriteDouble(long nativePtr, double val);
@RavenwoodThrow
private static native void nativeSignalExceptionForError(int error);
@FastNative
+ @RavenwoodRedirect
private static native void nativeWriteString8(long nativePtr, String val);
@FastNative
+ @RavenwoodRedirect
private static native void nativeWriteString16(long nativePtr, String val);
@FastNative
@RavenwoodThrow
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
@FastNative
+ @RavenwoodRedirect
private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
+ @RavenwoodRedirect
private static native byte[] nativeCreateByteArray(long nativePtr);
+ @RavenwoodRedirect
private static native boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen);
+ @RavenwoodRedirect
private static native byte[] nativeReadBlob(long nativePtr);
@CriticalNative
+ @RavenwoodRedirect
private static native int nativeReadInt(long nativePtr);
@CriticalNative
+ @RavenwoodRedirect
private static native long nativeReadLong(long nativePtr);
@CriticalNative
+ @RavenwoodRedirect
private static native float nativeReadFloat(long nativePtr);
@CriticalNative
+ @RavenwoodRedirect
private static native double nativeReadDouble(long nativePtr);
@FastNative
+ @RavenwoodRedirect
private static native String nativeReadString8(long nativePtr);
@FastNative
+ @RavenwoodRedirect
private static native String nativeReadString16(long nativePtr);
@FastNative
@RavenwoodThrow
private static native IBinder nativeReadStrongBinder(long nativePtr);
@FastNative
+ @RavenwoodRedirect
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
+ @RavenwoodRedirect
private static native long nativeCreate();
+ @RavenwoodRedirect
private static native void nativeFreeBuffer(long nativePtr);
+ @RavenwoodRedirect
private static native void nativeDestroy(long nativePtr);
+ @RavenwoodRedirect
private static native byte[] nativeMarshall(long nativePtr);
+ @RavenwoodRedirect
private static native void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
+ @RavenwoodRedirect
private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
+ @RavenwoodRedirect
private static native boolean nativeCompareDataInRange(
long ptrA, int offsetA, long ptrB, int offsetB, int length);
+ @RavenwoodRedirect
private static native void nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int offset, int length);
@CriticalNative
+ @RavenwoodRedirect
private static native boolean nativeHasFileDescriptors(long nativePtr);
+ @RavenwoodRedirect
private static native boolean nativeHasFileDescriptorsInRange(
long nativePtr, int offset, int length);
+ @RavenwoodRedirect
private static native boolean nativeHasBinders(long nativePtr);
+ @RavenwoodRedirect
private static native boolean nativeHasBindersInRange(
long nativePtr, int offset, int length);
@RavenwoodThrow
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index bb74a3e7f896fcf6e0c95a87e67ee3f950fbd460..398140dafc33e567e5dc39fcb8d9e787cb655f3b 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -18,6 +18,7 @@ package android.os;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1170,11 +1171,27 @@ public class RecoverySystem {
return removedSubsCount.get() == subscriptionInfos.size();
}
- /** {@hide} */
- public static void rebootPromptAndWipeUserData(Context context, String reason)
+ /**
+ * Reboot into recovery and prompt for wiping the device.
+ *
+ * This is used as last resort in case the device is not recoverable using
+ * other recovery steps. This first checks if fs-checkpoint is available, in
+ * which case we commit the checkpoint, otherwise it performs the reboot in
+ * recovery mode and shows user prompt for wiping the device.
+ *
+ * @param context the context to use.
+ * @param reason the reason to wipe.
+ *
+ * @throws IOException if something goes wrong.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RECOVERY)
+ @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
+ public static void rebootPromptAndWipeUserData(@NonNull Context context, @NonNull String reason)
throws IOException {
boolean checkpointing = false;
- boolean needReboot = false;
IVold vold = null;
try {
vold = IVold.Stub.asInterface(ServiceManager.checkService("vold"));
diff --git a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
index 79f229acbccb440fbf520fb21c4bae7f8f69c36e..80c24a9003e8867ae3b2ec3e58351febce794f47 100644
--- a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
@@ -19,8 +19,9 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.os.Handler;
-import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
@@ -37,8 +38,6 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.PriorityQueue;
-import java.util.PriorityQueue;
-import java.util.PriorityQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -50,9 +49,8 @@ import java.util.concurrent.atomic.AtomicLong;
*
You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
-@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "SemiConcurrentMessageQueue";
private static final boolean DEBUG = false;
@@ -338,11 +336,17 @@ public final class MessageQueue {
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private final AtomicInteger mNextBarrierToken = new AtomicInteger(1);
+ @RavenwoodRedirect
private static native long nativeInit();
+ @RavenwoodRedirect
private static native void nativeDestroy(long ptr);
+ @RavenwoodRedirect
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+ @RavenwoodRedirect
private static native void nativeWake(long ptr);
+ @RavenwoodRedirect
private static native boolean nativeIsPolling(long ptr);
+ @RavenwoodRedirect
private static native void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index c801fabf94278cc1aa5726e9d29ea8dab95a0c79..46ae9d8682ee97c61bc02b7a52495767f6c419e1 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -379,6 +379,14 @@ public final class SharedMemory implements Parcelable, Closeable {
private int mReferenceCount;
private MemoryRegistration(int size) {
+ // Round up to the nearest page size
+ final int PAGE_SIZE = OsConstants._SC_PAGE_SIZE;
+ if (PAGE_SIZE > 0) {
+ final int remainder = size % PAGE_SIZE;
+ if (remainder != 0) {
+ size += PAGE_SIZE - remainder;
+ }
+ }
mSize = size;
mReferenceCount = 1;
VMRuntime.getRuntime().registerNativeAllocation(mSize);
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 23bd30a21c4c0ddd947319e49e57734efba000cd..0ed1ab6c8d725adeb84fb63c9d052294c5461b6b 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -313,6 +313,15 @@ public final class SystemClock {
return System.nanoTime() / 1000L;
}
+ /**
+ * @see #currentNetworkTimeMillis(ITimeDetectorService)
+ * @hide
+ */
+ public static long currentNetworkTimeMillis() {
+ return currentNetworkTimeMillis(ITimeDetectorService.Stub
+ .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE)));
+ }
+
/**
* Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
* using a remote network source outside the device.
@@ -331,14 +340,14 @@ public final class SystemClock {
* at any time. Due to network delays, variations between servers, or local
* (client side) clock drift, the accuracy of the returned times cannot be
* guaranteed. In extreme cases, consecutive calls to {@link
- * #currentNetworkTimeMillis()} could return times that are out of order.
+ * #currentNetworkTimeMillis(ITimeDetectorService)} could return times that
+ * are out of order.
*
* @throws DateTimeException when no network time can be provided.
* @hide
*/
- public static long currentNetworkTimeMillis() {
- ITimeDetectorService timeDetectorService = ITimeDetectorService.Stub
- .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
+ public static long currentNetworkTimeMillis(
+ ITimeDetectorService timeDetectorService) {
if (timeDetectorService != null) {
UnixEpochTime time;
try {
@@ -380,16 +389,21 @@ public final class SystemClock {
* at any time. Due to network delays, variations between servers, or local
* (client side) clock drift, the accuracy of the returned times cannot be
* guaranteed. In extreme cases, consecutive calls to {@link
- * Clock#millis()} on the returned {@link Clock}could return times that are
+ * Clock#millis()} on the returned {@link Clock} could return times that are
* out of order.
*
* @throws DateTimeException when no network time can be provided.
*/
public static @NonNull Clock currentNetworkTimeClock() {
return new SimpleClock(ZoneOffset.UTC) {
+ private ITimeDetectorService mSvc;
@Override
public long millis() {
- return SystemClock.currentNetworkTimeMillis();
+ if (mSvc == null) {
+ mSvc = ITimeDetectorService.Stub
+ .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
+ }
+ return SystemClock.currentNetworkTimeMillis(mSvc);
}
};
}
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 0a386913de599b0b2f9e07652f1c789136693c4e..e53873b5622ecdeafe369bc6da16974cd609a74c 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -21,11 +21,13 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.util.Log;
import android.util.MutableInt;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -56,8 +58,7 @@ import java.util.function.Predicate;
*/
@SystemApi
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.SystemProperties_host")
+@RavenwoodRedirectionClass("SystemProperties_host")
public class SystemProperties {
private static final String TAG = "SystemProperties";
private static final boolean TRACK_KEY_ACCESS = false;
@@ -75,7 +76,7 @@ public class SystemProperties {
@UnsupportedAppUsage
@GuardedBy("sChangeCallbacks")
- private static final ArrayList sChangeCallbacks = new ArrayList();
+ static final ArrayList sChangeCallbacks = new ArrayList();
@GuardedBy("sRoReads")
private static final HashMap sRoReads =
@@ -102,30 +103,18 @@ public class SystemProperties {
}
/** @hide */
+ @RavenwoodRedirect
public static void init$ravenwood(Map values,
Predicate keyReadablePredicate, Predicate keyWritablePredicate) {
- native_init$ravenwood(values, keyReadablePredicate, keyWritablePredicate,
- SystemProperties::callChangeCallbacks);
- synchronized (sChangeCallbacks) {
- sChangeCallbacks.clear();
- }
+ throw RavenwoodEnvironment.notSupportedOnDevice();
}
/** @hide */
+ @RavenwoodRedirect
public static void reset$ravenwood() {
- native_reset$ravenwood();
- synchronized (sChangeCallbacks) {
- sChangeCallbacks.clear();
- }
+ throw RavenwoodEnvironment.notSupportedOnDevice();
}
- // These native methods are currently only implemented by Ravenwood, as it's the only
- // mechanism we have to jump to our RavenwoodNativeSubstitutionClass
- private static native void native_init$ravenwood(Map values,
- Predicate keyReadablePredicate, Predicate keyWritablePredicate,
- Runnable changeCallback);
- private static native void native_reset$ravenwood();
-
// The one-argument version of native_get used to be a regular native function. Nowadays,
// we use the two-argument form of native_get all the time, but we can't just delete the
// one-argument overload: apps use it via reflection, as the UnsupportedAppUsage annotation
@@ -137,34 +126,46 @@ public class SystemProperties {
@FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native String native_get(String key, String def);
@FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native int native_get_int(String key, int def);
@FastNative
@UnsupportedAppUsage
+ @RavenwoodRedirect
private static native long native_get_long(String key, long def);
@FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native boolean native_get_boolean(String key, boolean def);
@FastNative
+ @RavenwoodRedirect
private static native long native_find(String name);
@FastNative
+ @RavenwoodRedirect
private static native String native_get(long handle);
@CriticalNative
+ @RavenwoodRedirect
private static native int native_get_int(long handle, int def);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_get_long(long handle, long def);
@CriticalNative
+ @RavenwoodRedirect
private static native boolean native_get_boolean(long handle, boolean def);
// _NOT_ FastNative: native_set performs IPC and can block
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native void native_set(String key, String def);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @RavenwoodRedirect
private static native void native_add_change_callback();
+ @RavenwoodRedirect
private static native void native_report_sysprop_change();
/**
@@ -300,7 +301,7 @@ public class SystemProperties {
}
@SuppressWarnings("unused") // Called from native code.
- private static void callChangeCallbacks() {
+ static void callChangeCallbacks() {
ArrayList callbacks = null;
synchronized (sChangeCallbacks) {
//Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
diff --git a/core/java/android/os/TransactionTooLargeException.java b/core/java/android/os/TransactionTooLargeException.java
index 79892e060f49b2737dddaea199dec6323edece6a..6b7cb3310ee47610379a2768ae5a48bd1db3e574 100644
--- a/core/java/android/os/TransactionTooLargeException.java
+++ b/core/java/android/os/TransactionTooLargeException.java
@@ -47,7 +47,7 @@ import android.os.RemoteException;
* If possible, try to break up big requests into smaller pieces.
*
* If you are implementing a service, it may help to impose size or complexity
- * contraints on the queries that clients can perform. For example, if the result set
+ * constraints on the queries that clients can perform. For example, if the result set
* could become large, then don't allow the client to request more than a few records
* at a time. Alternately, instead of returning all of the available data all at once,
* return the essential information first and make the client ask for additional information
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 536eca6f9a0ffd2140018a00227c9e344e8d5775..f1ec0e4e9bdc0db071ad836edd19823737213e5e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1978,7 +1978,6 @@ public class UserManager {
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- @FlaggedApi(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED)
public static final String DISALLOW_SIM_GLOBALLY =
"no_sim_globally";
@@ -1999,7 +1998,6 @@ public class UserManager {
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- @FlaggedApi(android.app.admin.flags.Flags.FLAG_ASSIST_CONTENT_USER_RESTRICTION_ENABLED)
public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content";
/**
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index f02d4a9ce4a78eec31f1845ad5435dc3bbbec293..64a2dbcb6a838cb23ea838e2c0228a9fe4d5ca11 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -540,6 +540,17 @@ public abstract class VibrationEffect implements Parcelable {
/** @hide */
public abstract void validate();
+
+ /**
+ * If supported, truncate the length of this vibration effect to the provided length and return
+ * the result. Will always return null for repeating effects.
+ *
+ * @return The desired effect, or {@code null} if truncation is not applicable.
+ * @hide
+ */
+ @Nullable
+ public abstract VibrationEffect cropToLengthOrNull(int length);
+
/**
* Gets the estimated duration of the vibration in milliseconds.
*
@@ -866,6 +877,30 @@ public abstract class VibrationEffect implements Parcelable {
}
}
+ /** @hide */
+ @Override
+ @Nullable
+ public VibrationEffect cropToLengthOrNull(int length) {
+ // drop repeating effects
+ if (mRepeatIndex >= 0) {
+ return null;
+ }
+
+ int segmentCount = mSegments.size();
+ if (segmentCount <= length) {
+ return this;
+ }
+
+ ArrayList truncated = new ArrayList(mSegments.subList(0, length));
+ Composed updated = new Composed(truncated, mRepeatIndex);
+ try {
+ updated.validate();
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ return updated;
+ }
+
@Override
public long getDuration() {
if (mRepeatIndex >= 0) {
@@ -1150,6 +1185,13 @@ public abstract class VibrationEffect implements Parcelable {
"Vendor effect bundle must be non-empty");
}
+ /** @hide */
+ @Override
+ @Nullable
+ public VibrationEffect cropToLengthOrNull(int length) {
+ return null;
+ }
+
@Override
public long getDuration() {
return -1; // UNKNOWN
diff --git a/core/java/android/permission/TEST_MAPPING b/core/java/android/permission/TEST_MAPPING
index a15d9bc1b485b56dd23d6af63023eeffd8412d46..b317b80d56776c11ed5691cd9fd4a6eb78b67835 100644
--- a/core/java/android/permission/TEST_MAPPING
+++ b/core/java/android/permission/TEST_MAPPING
@@ -1,15 +1,7 @@
{
"presubmit": [
{
- "name": "CtsPermissionTestCases",
- "options": [
- {
- "include-filter": "android.permission.cts.PermissionControllerTest"
- },
- {
- "include-filter": "android.permission.cts.RuntimePermissionPresentationInfoTest"
- }
- ]
+ "name": "CtsPermissionTestCases_Platform"
}
],
"postsubmit": [
@@ -36,4 +28,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 85d23259ffbb1db732808359738f3fdd07a3ae63..0a05f704f5239e85f9167ea64e75c37c2e275cff 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1974,7 +1974,7 @@ public final class Settings {
/**
* Activity Action: Show Notification Policy access settings.
*
- * Users can grant and deny access to Notification Policy (DND / Priority Modes) configuration
+ * Users can grant and deny access to Notification Policy (DND / Modes) configuration
* from here. Managed profiles cannot grant Notification Policy access.
* See {@link android.app.NotificationManager#isNotificationPolicyAccessGranted()} for more
* details.
@@ -5150,13 +5150,19 @@ public final class Settings {
public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
/**
- * The screen backlight brightness between 0 and 255.
+ * The screen backlight brightness between 1 (minimum) and 255 (maximum).
+ *
+ * Use {@link android.view.WindowManager.LayoutParams#screenBrightness} to set the screen
+ * brightness instead.
*/
@Readable
public static final String SCREEN_BRIGHTNESS = "screen_brightness";
/**
- * Control whether to enable automatic brightness mode.
+ * Controls whether to enable automatic brightness mode. Value can be set to
+ * {@link #SCREEN_BRIGHTNESS_MODE_MANUAL} or {@link #SCREEN_BRIGHTNESS_MODE_AUTOMATIC}.
+ * If {@link #SCREEN_BRIGHTNESS_MODE_AUTOMATIC} is set, the system may change
+ * {@link #SCREEN_BRIGHTNESS} automatically.
*/
@Readable
public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
@@ -19666,6 +19672,8 @@ public final class Settings {
public static final int PAIRED_DEVICE_OS_TYPE_ANDROID = 1;
/** @hide */
public static final int PAIRED_DEVICE_OS_TYPE_IOS = 2;
+ /** @hide */
+ public static final int PAIRED_DEVICE_OS_TYPE_NONE = 3;
/**
* The bluetooth settings selected BLE role for the companion.
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index 1f1a35117f72151c7247504611d0e1abea00bdba..2b75493a369ed0139c1287061a46e56395620039 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -1,6 +1,22 @@
package: "android.service.chooser"
container: "system"
+flag {
+ name: "chooser_album_text"
+ is_exported: true
+ namespace: "intentresolver"
+ description: "Flag controlling the album text subtype hint for sharesheet"
+ bug: "323380224"
+}
+
+flag {
+ name: "enable_sharesheet_metadata_extra"
+ is_exported: true
+ namespace: "intentresolver"
+ description: "This flag enables sharesheet metadata to be displayed to users."
+ bug: "318942069"
+}
+
flag {
name: "chooser_payload_toggling"
is_exported: true
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 013ec5f3576179dc43a9add1180277b9122ef10b..711c41498929cc5e951490da62f19c3f9b9e564e 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -51,11 +51,14 @@ public abstract class DreamOverlayService extends Service {
*/
private Executor mExecutor;
+ private Boolean mCurrentRedirectToWake;
+
// An {@link IDreamOverlayClient} implementation that identifies itself when forwarding
// requests to the {@link DreamOverlayService}
private static class OverlayClient extends IDreamOverlayClient.Stub {
private final WeakReference mService;
private boolean mShowComplications;
+ private boolean mIsPreview;
private ComponentName mDreamComponent;
IDreamOverlayCallback mDreamOverlayCallback;
@@ -73,9 +76,11 @@ public abstract class DreamOverlayService extends Service {
@Override
public void startDream(WindowManager.LayoutParams params, IDreamOverlayCallback callback,
- String dreamComponent, boolean shouldShowComplications) throws RemoteException {
+ String dreamComponent, boolean isPreview, boolean shouldShowComplications)
+ throws RemoteException {
mDreamComponent = ComponentName.unflattenFromString(dreamComponent);
mShowComplications = shouldShowComplications;
+ mIsPreview = isPreview;
mDreamOverlayCallback = callback;
applyToDream(dreamOverlayService -> dreamOverlayService.startDream(this, params));
}
@@ -122,6 +127,10 @@ public abstract class DreamOverlayService extends Service {
return mShowComplications;
}
+ private boolean isDreamInPreviewMode() {
+ return mIsPreview;
+ }
+
private ComponentName getComponent() {
return mDreamComponent;
}
@@ -132,6 +141,10 @@ public abstract class DreamOverlayService extends Service {
mExecutor.execute(() -> {
endDreamInternal(mCurrentClient);
mCurrentClient = client;
+ if (Flags.dreamWakeRedirect() && mCurrentRedirectToWake != null) {
+ mCurrentClient.redirectWake(mCurrentRedirectToWake);
+ }
+
onStartDream(params);
});
}
@@ -282,8 +295,10 @@ public abstract class DreamOverlayService extends Service {
return;
}
+ mCurrentRedirectToWake = redirect;
+
if (mCurrentClient == null) {
- throw new IllegalStateException("redirected wake with no dream present");
+ return;
}
mCurrentClient.redirectWake(redirect);
@@ -295,7 +310,6 @@ public abstract class DreamOverlayService extends Service {
*
* @hide
*/
- @FlaggedApi(Flags.FLAG_DREAM_WAKE_REDIRECT)
public void onWakeRequested() {
}
@@ -311,6 +325,19 @@ public abstract class DreamOverlayService extends Service {
return mCurrentClient.shouldShowComplications();
}
+ /**
+ * Returns whether dream is in preview mode.
+ */
+ @FlaggedApi(Flags.FLAG_PUBLISH_PREVIEW_STATE_TO_OVERLAY)
+ public final boolean isDreamInPreviewMode() {
+ if (mCurrentClient == null) {
+ throw new IllegalStateException(
+ "requested if preview when no dream active");
+ }
+
+ return mCurrentClient.isDreamInPreviewMode();
+ }
+
/**
* Returns the active dream component.
* @hide
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index c3585e3c5288b779f14c37b7e39b13685b014c06..ce31e1ea7e38412e9f1a97191d7b51487225866a 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1701,6 +1701,7 @@ public class DreamService extends Service implements Window.Callback {
try {
overlay.startDream(mWindow.getAttributes(), mOverlayCallback,
mDreamComponent.flattenToString(),
+ mPreviewMode,
mShouldShowComplications);
} catch (RemoteException e) {
Log.e(mTag, "could not send window attributes:" + e);
diff --git a/core/java/android/service/dreams/IDreamOverlayClient.aidl b/core/java/android/service/dreams/IDreamOverlayClient.aidl
index 0eb15a07edf7d2f97451c5e2cc7e0ba0fec21ce0..e9df402c6d07c9fae578b251f3a4c0ab13c41d1f 100644
--- a/core/java/android/service/dreams/IDreamOverlayClient.aidl
+++ b/core/java/android/service/dreams/IDreamOverlayClient.aidl
@@ -31,11 +31,12 @@ interface IDreamOverlayClient {
* @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the
* dream.
* @param dreamComponent The component name of the dream service requesting overlay.
+ * @param isPreview Whether the dream is in preview mode.
* @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock
* and weather.
*/
void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
- in String dreamComponent, in boolean shouldShowComplications);
+ in String dreamComponent, in boolean isPreview, in boolean shouldShowComplications);
/** Called when the dream is waking, to do any exit animations */
void wakeUp();
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index 83e0adfca3beb48da8d556fbb2118a4870c3ec0b..72f2de805474adb2da0892e29913bed681b59419 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -57,3 +57,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "publish_preview_state_to_overlay"
+ namespace: "systemui"
+ description: "send preview information from dream to overlay"
+ bug: "333734282"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 918e591069fb0d59929920f95ba2af2c8672375d..3d8d933cbbcc4220f1c26a21bb40d9ffc2a7256d 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1044,23 +1044,19 @@ public class ZenModeConfig implements Parcelable {
rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
} else if (MANUAL_TAG.equals(tag)) {
- ZenRule manualRule = readRuleXml(parser);
- if (manualRule != null) {
- rt.manualRule = manualRule;
-
- // Manual rule may be present prior to modes_ui if it were on, but in that
- // case it would not have a set policy, so make note of the need to set
- // it up later.
- readManualRule = true;
- if (rt.manualRule.zenPolicy == null) {
- readManualRuleWithoutPolicy = true;
- }
+ rt.manualRule = readRuleXml(parser);
+ // Manual rule may be present prior to modes_ui if it were on, but in that
+ // case it would not have a set policy, so make note of the need to set
+ // it up later.
+ readManualRule = true;
+ if (rt.manualRule.zenPolicy == null) {
+ readManualRuleWithoutPolicy = true;
}
} else if (AUTOMATIC_TAG.equals(tag)
|| (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
final String id = parser.getAttributeValue(null, RULE_ATT_ID);
- final ZenRule automaticRule = readRuleXml(parser);
- if (id != null && automaticRule != null) {
+ if (id != null) {
+ final ZenRule automaticRule = readRuleXml(parser);
automaticRule.id = id;
if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) {
String deletedRuleKey = deletedRuleKey(automaticRule);
@@ -1177,16 +1173,13 @@ public class ZenModeConfig implements Parcelable {
out.endTag(null, ZEN_TAG);
}
+ @NonNull
public static ZenRule readRuleXml(TypedXmlPullParser parser) {
final ZenRule rt = new ZenRule();
rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
- rt.zenMode = tryParseZenMode(zen, -1);
- if (rt.zenMode == -1) {
- Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
- return null;
- }
+ rt.zenMode = tryParseZenMode(zen, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
@@ -2939,7 +2932,7 @@ public class ZenModeConfig implements Parcelable {
}
}
- // TODO: b/333527800 - Rename to isActive()
+ // TODO: b/363193376 - Rename to isActive()
public boolean isAutomaticActive() {
if (Flags.modesApi() && Flags.modesUi()) {
if (!enabled || getPkg() == null) {
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 2669391b4d4521c49a9fffd03c6d442987215662..be0d7b3a72f209fee8554356c1534841d8de6c99 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -392,6 +392,46 @@ public final class ZenPolicy implements Parcelable {
mAllowChannels = allowChannels;
}
+ /**
+ * Base Zen Policy used when {@link android.app.NotificationManager#setInterruptionFilter} is
+ * called with {@link android.app.NotificationManager#INTERRUPTION_FILTER_ALARMS} or an
+ * {@link android.app.AutomaticZenRule} specifies said filter.
+ *
+ * Note that visual effects for filtered notifications are unset in this base
+ * policy, so should be merged on top of the default policy's visual effects (see
+ * {@link #overwrittenWith(ZenPolicy)}).
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static ZenPolicy getBasePolicyInterruptionFilterAlarms() {
+ return new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowPriorityChannels(false)
+ .build();
+ }
+
+ /**
+ * Base Zen Policy used when {@link android.app.NotificationManager#setInterruptionFilter} is
+ * called with {@link android.app.NotificationManager#INTERRUPTION_FILTER_NONE} or an
+ * {@link android.app.AutomaticZenRule} specifies said filter.
+ *
+ *
Note that visual effects for filtered notifications are unset in this base
+ * policy, so it should be merged on top of the device default policy's visual effects (see
+ * {@link #overwrittenWith(ZenPolicy)}).
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static ZenPolicy getBasePolicyInterruptionFilterNone() {
+ return new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowPriorityChannels(false)
+ .build();
+ }
+
/**
* Conversation type that can bypass DND.
* @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index 5d84d17bdb6e4ee797abce90d124c721be020f5a..bbe9cdbbce9fef9283d258f5878029d37c4b90a1 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -34,20 +34,6 @@ public class ClientFlags {
return TextFlags.isFeatureEnabled(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN);
}
- /**
- * @see Flags#phraseStrictFallback()
- */
- public static boolean phraseStrictFallback() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_PHRASE_STRICT_FALLBACK);
- }
-
- /**
- * @see Flags#useBoundsForWidth()
- */
- public static boolean useBoundsForWidth() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_USE_BOUNDS_FOR_WIDTH);
- }
-
/**
* @see Flags#fixLineHeightForLocale()
*/
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 9e02460d263730e53f5cfef829e0bbf5a17ffb36..0f1b031a8fad19141990fd3e69b5d33e2d888fb7 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -56,8 +56,6 @@ public final class TextFlags {
*/
public static final String[] TEXT_ACONFIGS_FLAGS = {
Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN,
- Flags.FLAG_PHRASE_STRICT_FALLBACK,
- Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE,
Flags.FLAG_ICU_BIDI_MIGRATION,
Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU,
@@ -70,8 +68,6 @@ public final class TextFlags {
*/
public static final boolean[] TEXT_ACONFIG_DEFAULT_VALUE = {
Flags.noBreakNoHyphenationSpan(),
- Flags.phraseStrictFallback(),
- Flags.useBoundsForWidth(),
Flags.fixLineHeightForLocale(),
Flags.icuBidiMigration(),
Flags.fixMisalignedContextMenu(),
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index d33c95e066773b1f138199a4d53379d717714d07..b2be1a7ef3512f39ea52b6c5fa6b33d000b5f1c9 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -1,14 +1,6 @@
package: "com.android.text.flags"
container: "system"
-flag {
- name: "vendor_custom_locale_fallback"
- namespace: "text"
- description: "A feature flag that adds custom locale fallback to the vendor customization XML. This enables vendors to add their locale specific fonts, e.g. Japanese font."
- is_fixed_read_only: true
- bug: "278768958"
-}
-
flag {
name: "new_fonts_fallback_xml"
is_exported: true
@@ -19,13 +11,6 @@ flag {
bug: "281769620"
}
-flag {
- name: "fix_double_underline"
- namespace: "text"
- description: "Feature flag for fixing double underline because of the multiple font used in the single line."
- bug: "297336724"
-}
-
flag {
name: "fix_line_height_for_locale"
is_exported: true
@@ -65,13 +50,6 @@ flag {
}
}
-flag {
- name: "phrase_strict_fallback"
- namespace: "text"
- description: "Feature flag for automatic fallback from phrase based line break to strict line break."
- bug: "281970875"
-}
-
flag {
name: "use_bounds_for_width"
is_exported: true
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index 04dba462bc1fbd5fa4b06b18680ba6ff8cf43523..fb1bd1703ce64647750a4f52cf7837ac6052140f 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -47,6 +47,22 @@ flag {
bug: "323165543"
}
+flag {
+ name: "perfetto_wm_dump"
+ namespace: "windowing_tools"
+ description: "Migrate WindowManager dump to Perfetto"
+ is_fixed_read_only: true
+ bug: "323165543"
+}
+
+flag {
+ name: "perfetto_wm_dump_cts"
+ namespace: "windowing_tools"
+ description: "Migrate WindowManager dump in CTS tests to Perfetto"
+ is_fixed_read_only: true
+ bug: "323165543"
+}
+
flag {
name: "client_side_proto_logging"
namespace: "windowing_tools"
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index 4de7b62521d1a8e4fcc1b30ec0fa32074ab88f6e..b65471d23c5c413a27fccc039b5f870ce77461fb 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -16,6 +16,8 @@
package android.tracing.perfetto;
+import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.util.proto.ProtoInputStream;
/**
@@ -41,6 +43,7 @@ public abstract class DataSource args) {
return null;
}
@@ -112,6 +115,7 @@ public abstract class DataSource args) {
return null;
@@ -141,6 +145,7 @@ public abstract class DataSource output)
throws IOException;
@@ -391,6 +400,7 @@ public class EventLog {
* @hide
*/
@SystemApi
+ @RavenwoodThrow
public static native void readEventsOnWrapping(int[] tags, long timestamp,
Collection output)
throws IOException;
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index fe536a6e4e68b18a6ef34765d3a94dc13eb10528..22583acb75ce14b4446c564ebff515fd1c8350b6 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -19,6 +19,7 @@ package android.util;
import android.annotation.HalfFloat;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import libcore.util.FP16;
@@ -92,6 +93,7 @@ import libcore.util.FP16;
* This table shows that numbers higher than 1024 lose all fractional precision.
*/
@SuppressWarnings("SimplifiableIfStatement")
+@RavenwoodKeepWholeClass
public final class Half extends Number implements Comparable {
/**
* The number of bits used to represent a half-precision float value.
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index feb80cc28e774e9d1907c47a8fff04b5da433b9b..ca5798a80a105806be7b3e53509385750382a72d 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -112,6 +112,11 @@ public final class LocalLog {
}
}
+ // @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ public synchronized void clear() {
+ mLog.clear();
+ }
+
public static class ReadOnlyLocalLog {
private final LocalLog mLog;
ReadOnlyLocalLog(LocalLog log) {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 1f7ed8be357cce1fc4405a07eb104fa3860ee476..82c52a6e893156d78d61c6f3968da5ad743d360d 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -314,6 +314,8 @@ public final class Display {
* @hide
* @see #getFlags()
*/
+ @SuppressLint("UnflaggedApi") // Promotion to TestApi
+ @TestApi
public static final int FLAG_ALWAYS_UNLOCKED = 1 << 9;
/**
@@ -323,6 +325,8 @@ public final class Display {
* @hide
* @see #getFlags()
*/
+ @SuppressLint("UnflaggedApi") // Promotion to TestApi
+ @TestApi
public static final int FLAG_TOUCH_FEEDBACK_DISABLED = 1 << 10;
/**
@@ -336,6 +340,8 @@ public final class Display {
* @see #FLAG_TRUSTED
* @hide
*/
+ @SuppressLint("UnflaggedApi") // Promotion to TestApi
+ @TestApi
public static final int FLAG_OWN_FOCUS = 1 << 11;
/**
@@ -642,6 +648,8 @@ public final class Display {
* @hide
*/
// TODO (b/114338689): Remove the flag and use WindowManager#REMOVE_CONTENT_MODE_DESTROY
+ @SuppressLint("UnflaggedApi") // Promotion to TestApi
+ @TestApi
public static final int REMOVE_MODE_DESTROY_CONTENT = 1;
/** @hide */
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index c7e93c19484fc139cc511b0d41f2cecff81ead8a..b80146505a1bd2364a70e19a10b02f0e55dd0bf6 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -149,15 +149,17 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
private void setPreCommitProgress(float progress) {
if (isHideAnimationInProgress()) return;
+ setInterpolatedProgress(BACK_GESTURE.getInterpolation(progress) * PEEK_FRACTION);
+ }
+
+ private void setInterpolatedProgress(float progress) {
if (mWindowInsetsAnimationController != null) {
float hiddenY = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
float shownY = mWindowInsetsAnimationController.getShownStateInsets().bottom;
float imeHeight = shownY - hiddenY;
- float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
- int newY = (int) (imeHeight - interpolatedProgress * (imeHeight * PEEK_FRACTION));
+ int newY = (int) (imeHeight - progress * imeHeight);
if (mStartRootScrollY != 0) {
- mViewRoot.setScrollY(
- (int) (mStartRootScrollY * (1 - interpolatedProgress * PEEK_FRACTION)));
+ mViewRoot.setScrollY((int) (mStartRootScrollY * (1 - progress)));
}
mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, newY), 1f,
progress);
@@ -171,21 +173,14 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
return;
}
mTriggerBack = triggerBack;
- int currentBottomInset = mWindowInsetsAnimationController.getCurrentInsets().bottom;
- int targetBottomInset;
- if (triggerBack) {
- targetBottomInset = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
- } else {
- targetBottomInset = mWindowInsetsAnimationController.getShownStateInsets().bottom;
- }
- mPostCommitAnimator = ValueAnimator.ofFloat(currentBottomInset, targetBottomInset);
+ float targetProgress = triggerBack ? 1f : 0f;
+ mPostCommitAnimator = ValueAnimator.ofFloat(
+ BACK_GESTURE.getInterpolation(mLastProgress) * PEEK_FRACTION, targetProgress);
mPostCommitAnimator.setInterpolator(
triggerBack ? STANDARD_ACCELERATE : EMPHASIZED_DECELERATE);
mPostCommitAnimator.addUpdateListener(animation -> {
- int bottomInset = (int) ((float) animation.getAnimatedValue());
if (mWindowInsetsAnimationController != null) {
- mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, bottomInset),
- 1f, animation.getAnimatedFraction());
+ setInterpolatedProgress((float) animation.getAnimatedValue());
} else {
reset();
}
@@ -213,14 +208,8 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
notifyHideIme();
// requesting IME as invisible during post-commit
mInsetsController.setRequestedVisibleTypes(0, ime());
- // Changes the animation state. This also notifies RootView of changed insets, which
- // causes it to reset its scrollY to 0f (animated) if it was panned
mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
}
- if (mStartRootScrollY != 0 && !triggerBack) {
- // This causes RootView to update its scroll back to the panned position
- mInsetsController.getHost().notifyInsetsChanged();
- }
}
private void notifyHideIme() {
@@ -282,6 +271,10 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
return mPostCommitAnimator != null && mTriggerBack;
}
+ boolean isAnimationInProgress() {
+ return mIsPreCommitAnimationInProgress || mWindowInsetsAnimationController != null;
+ }
+
/**
* Dump information about this ImeBackAnimationController
*
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 6343313b2e0128c3fbf4d6731af0c8a4b0193a8d..e90b1c0fc16749cdfd883ba633c380b36b404f34 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -70,7 +70,14 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
"ImeInsetsSourceConsumer#onAnimationFinished",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
- boolean insetsChanged = super.onAnimationStateChanged(running);
+ boolean insetsChanged = false;
+ if (Flags.predictiveBackIme() && !running && isShowRequested()
+ && mAnimationState == ANIMATION_STATE_HIDE) {
+ // A user controlled hide animation may have ended in the shown state (e.g.
+ // cancelled predictive back animation) -> Insets need to be reset to shown.
+ insetsChanged |= applyLocalVisibilityOverride();
+ }
+ insetsChanged |= super.onAnimationStateChanged(running);
if (running && !isShowRequested()
&& mController.isPredictiveBackImeHideAnimInProgress()) {
// IME predictive back animation switched from pre-commit to post-commit.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b1df51f7affae2d7498301549598317e209d740b..8fdf91a2d87cf3bd00aaa353bf6e5192e398f0b6 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -488,7 +488,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public Interpolator getInsetsInterpolator(boolean hasZeroInsetsIme) {
if ((mRequestedTypes & ime()) != 0) {
- if (mHasAnimationCallbacks && hasZeroInsetsIme) {
+ if (mHasAnimationCallbacks && !hasZeroInsetsIme) {
return SYNC_IME_INTERPOLATOR;
} else if (mShow) {
return LINEAR_OUT_SLOW_IN_INTERPOLATOR;
@@ -536,7 +536,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public long getDurationMs(boolean hasZeroInsetsIme) {
if ((mRequestedTypes & ime()) != 0) {
- if (mHasAnimationCallbacks && hasZeroInsetsIme) {
+ if (mHasAnimationCallbacks && !hasZeroInsetsIme) {
return ANIMATION_DURATION_SYNC_IME_MS;
} else {
return ANIMATION_DURATION_UNSYNC_IME_MS;
@@ -1197,7 +1197,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
pendingRequest.listener, null /* frame */, true /* fromIme */,
pendingRequest.mInsetsAnimationSpec,
pendingRequest.animationType, pendingRequest.layoutInsetsDuringAnimation,
- pendingRequest.useInsetsAnimationThread, statsToken);
+ pendingRequest.useInsetsAnimationThread, statsToken,
+ false /* fromPredictiveBack */);
}
@Override
@@ -1307,7 +1308,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsetsAnimationControlListener listener,
boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
@AnimationType int animationType, boolean fromPredictiveBack) {
- if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) {
+ if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0
+ || (fromPredictiveBack && ((mRequestedVisibleTypes & ime()) == 0))) {
+ // abort if insets are uncontrollable or if control request is from predictive back but
+ // there is already a hide anim in progress
listener.onCancelled(null);
return;
}
@@ -1330,7 +1334,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// TODO(b/342111149): Create statsToken here once ImeTracker#onStart becomes async.
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, spec,
animationType, getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
- false /* useInsetsAnimationThread */, null);
+ false /* useInsetsAnimationThread */, null, fromPredictiveBack);
}
private void controlAnimationUnchecked(@InsetsType int types,
@@ -1338,7 +1342,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+ boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken,
+ boolean fromPredictiveBack) {
final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
// Basically, we accept the requested visibilities from the upstream callers...
@@ -1348,7 +1353,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// rejecting showing IME.
controlAnimationUncheckedInner(types, cancellationSignal, listener, frame, fromIme,
insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
- useInsetsAnimationThread, statsToken);
+ useInsetsAnimationThread, statsToken, fromPredictiveBack);
// We are finishing setting the requested visible types. Report them to the server
// and/or the app.
@@ -1360,7 +1365,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+ boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken,
+ boolean fromPredictiveBack) {
if ((types & mTypesBeingCancelled) != 0) {
final boolean monitoredAnimation =
animationType == ANIMATION_TYPE_SHOW || animationType == ANIMATION_TYPE_HIDE;
@@ -1446,7 +1452,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
} else {
Pair typesReadyPair = collectSourceControls(
- fromIme, types, controls, animationType, statsToken);
+ fromIme, types, controls, animationType, statsToken, fromPredictiveBack);
typesReady = typesReadyPair.first;
boolean imeReady = typesReadyPair.second;
if (DEBUG) {
@@ -1582,7 +1588,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
*/
private Pair collectSourceControls(boolean fromIme, @InsetsType int types,
SparseArray controls, @AnimationType int animationType,
- @Nullable ImeTracker.Token statsToken) {
+ @Nullable ImeTracker.Token statsToken, boolean fromPredictiveBack) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS);
@@ -1594,7 +1600,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
continue;
}
boolean show = animationType == ANIMATION_TYPE_SHOW
- || animationType == ANIMATION_TYPE_USER;
+ || (animationType == ANIMATION_TYPE_USER
+ && (!fromPredictiveBack || !mHost.hasAnimationCallbacks()));
boolean canRun = true;
if (show) {
// Show request
@@ -1617,7 +1624,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
break;
}
} else {
- consumer.requestHide(fromIme, statsToken);
+ consumer.requestHide(fromIme
+ || (fromPredictiveBack && mHost.hasAnimationCallbacks()), statsToken);
}
if (!canRun) {
if (WARN) Log.w(TAG, String.format(
@@ -1672,9 +1680,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
@InsetsType int types, boolean fromPredictiveBack) {
- if (fromPredictiveBack) {
- // When insets are animated by predictive back, we want insets to be shown to prevent a
- // jump cut from shown to hidden at the start of the predictive back animation
+ if (fromPredictiveBack && !mHost.hasAnimationCallbacks()) {
+ // When insets are animated by predictive back and the app does not have an animation
+ // callback, we want insets to be shown to prevent a jump cut from shown to hidden at
+ // the start of the predictive back animation
return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
}
// Generally, we want to layout the opposite of the current state. This is to make animation
@@ -2021,7 +2030,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
listener /* insetsAnimationSpec */,
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
- !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken);
+ !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken,
+ false /* fromPredictiveBack */);
}
/**
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 477e35b6e65557adf3f98391635ed1dc36d57ced..391d757365e6802dba8f7830884503a12d544a2a 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -310,21 +310,22 @@ public class InsetsSourceConsumer {
}
final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
+ // If we don't have control or the leash (in case of the IME), we enforce the
+ // visibility to be hidden, as otherwise we would let the app know too early.
+ if (mSourceControl == null) {
+ if (DEBUG) {
+ Log.d(TAG, TextUtils.formatSimple(
+ "applyLocalVisibilityOverride: No control in %s for type %s, "
+ + "requestedVisible=%s",
+ mController.getHost().getRootViewTitle(),
+ WindowInsets.Type.toString(mType), requestedVisible));
+ }
+ return false;
+ }
if (Flags.refactorInsetsController()) {
- // If we don't have control or the leash (in case of the IME), we enforce the
- // visibility to be hidden, as otherwise we would let the app know too early.
- if (mSourceControl == null) {
- if (DEBUG) {
- Log.d(TAG, TextUtils.formatSimple(
- "applyLocalVisibilityOverride: No control in %s for type %s, "
- + "requestedVisible=%s",
- mController.getHost().getRootViewTitle(),
- WindowInsets.Type.toString(mType), requestedVisible));
- }
- return false;
- // TODO(b/323136120) add a flag to the control, to define whether a leash is needed
- } else if (mId != InsetsSource.ID_IME_CAPTION_BAR
- && mSourceControl.getLeash() == null) {
+ // TODO(b/323136120) add a flag to the control, to define whether a leash is
+ // needed and make it generic for all types
+ if (mId == InsetsSource.ID_IME && mSourceControl.getLeash() == null) {
if (DEBUG) {
Log.d(TAG, TextUtils.formatSimple(
"applyLocalVisibilityOverride: Set the source visibility to false, as"
@@ -338,16 +339,6 @@ public class InsetsSourceConsumer {
// changed state
return wasVisible;
}
- } else {
- // If we don't have control, we are not able to change the visibility.
- if (mSourceControl == null) {
- if (DEBUG) {
- Log.d(TAG, "applyLocalVisibilityOverride: No control in "
- + mController.getHost().getRootViewTitle()
- + " requestedVisible=" + requestedVisible);
- }
- return false;
- }
}
if (source.isVisible() == requestedVisible) {
return false;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9e4b27d3fa55fa79f400a42c1533be72136dfbac..2dda835436bc6ff7a6a95061133fbce4e08417fe 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -445,16 +445,20 @@ public final class SurfaceControl implements Parcelable {
// Jank due to unknown reasons.
public static final int UNKNOWN = 0x80;
- public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs) {
+ public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs,
+ long scheduledAppFrameTimeNs, long actualAppFrameTimeNs) {
this.frameVsyncId = frameVsyncId;
this.jankType = jankType;
this.frameIntervalNs = frameIntervalNs;
-
+ this.scheduledAppFrameTimeNs = scheduledAppFrameTimeNs;
+ this.actualAppFrameTimeNs = actualAppFrameTimeNs;
}
public final long frameVsyncId;
public final @JankType int jankType;
public final long frameIntervalNs;
+ public final long scheduledAppFrameTimeNs;
+ public final long actualAppFrameTimeNs;
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e81f32e1e64b970910d508cc230a856309e613b6..7b4ea41554f1fbeef164220844740ed42f286de2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -141,7 +141,6 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.Vibrator;
import android.service.credentials.CredentialProviderService;
import android.sysprop.DisplayProperties;
import android.text.InputType;
@@ -27377,6 +27376,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ /**
+ * Modifiers the input matrix such that it maps root view's coordinates to view-local
+ * coordinates.
+ *
+ * @param matrix input matrix to modify
+ * @hide
+ */
+ public void transformMatrixRootToLocal(@NonNull Matrix matrix) {
+ final ViewParent parent = mParent;
+ if (parent instanceof final View vp) {
+ vp.transformMatrixRootToLocal(matrix);
+ matrix.postTranslate(vp.mScrollX, vp.mScrollY);
+ }
+ // This method is different from transformMatrixToLocal that it doesn't perform any
+ // transformation for ViewRootImpl
+
+ matrix.postTranslate(-mLeft, -mTop);
+
+ if (!hasIdentityMatrix()) {
+ matrix.postConcat(getInverseMatrix());
+ }
+ }
+
/**
* @hide
*/
@@ -34156,7 +34178,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
* Keep in mind that the preferred frame rate affects the frame rate for the next frame,
* so use this method carefully. It's important to note that the preference is valid as
- * long as the View is invalidated.
+ * long as the View is invalidated. Please also be aware that the requested frame rate
+ * will not propagate to child views when this API is used on a ViewGroup.
*
* @param frameRate the preferred frame rate of the view.
*/
@@ -34175,6 +34198,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
* REQUESTED_FRAME_RATE_CATEGORY_NORMAL, and REQUESTED_FRAME_RATE_CATEGORY_HIGH.
* Note that the frame rate value is valid as long as the View is invalidated.
+ * Please also be aware that the requested frame rate will not propagate to
+ * child views when this API is used on a ViewGroup.
*
* @return REQUESTED_FRAME_RATE_CATEGORY_DEFAULT by default,
* or value passed to {@link #setRequestedFrameRate(float)}.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index fb2b8b9c280c67fde3f4c04dc53e9da4788bc5ef..63bf392b5ef15a9992fdc7e16c9421f17531f2ba 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -373,6 +373,7 @@ public class ViewConfiguration {
private final int mMaximumDrawingCacheSize;
private final int mOverscrollDistance;
private final int mOverflingDistance;
+ private final boolean mViewTouchScreenHapticScrollFeedbackEnabled;
@UnsupportedAppUsage
private final boolean mFadingMarqueeEnabled;
private final long mGlobalActionsKeyTimeout;
@@ -437,6 +438,7 @@ public class ViewConfiguration {
mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND;
mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND;
mPreferKeepClearForFocusEnabled = false;
+ mViewTouchScreenHapticScrollFeedbackEnabled = false;
}
/**
@@ -588,6 +590,12 @@ public class ViewConfiguration {
mViewBasedRotaryEncoderScrollHapticsEnabledConfig =
res.getBoolean(
com.android.internal.R.bool.config_viewBasedRotaryEncoderHapticsEnabled);
+ mViewTouchScreenHapticScrollFeedbackEnabled =
+ Flags.enableTouchScrollFeedback()
+ ? res.getBoolean(
+ com.android.internal.R.bool
+ .config_viewTouchScreenHapticScrollFeedbackEnabled)
+ : false;
}
/**
@@ -1285,6 +1293,10 @@ public class ViewConfiguration {
return mRotaryEncoderHapticScrollFeedbackEnabled;
}
+ if ((source & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+ return mViewTouchScreenHapticScrollFeedbackEnabled;
+ }
+
return false;
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6f8838619808eb603e7836e4241199baa4053f5d..3b5286a04b3d8ce8ffc87c47214484cf78cb461f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3093,74 +3093,74 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
- final boolean handled;
-
- // Canceling motions is a special case. We don't need to perform any transformations
- // or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
- if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
- event.setAction(MotionEvent.ACTION_CANCEL);
- if (child == null) {
- handled = super.dispatchTouchEvent(event);
- } else {
- handled = child.dispatchTouchEvent(event);
+ try {
+ final boolean handled;
+ if (cancel) {
+ event.setAction(MotionEvent.ACTION_CANCEL);
}
- event.setAction(oldAction);
- return handled;
- }
- // Calculate the number of pointers to deliver.
- final int oldPointerIdBits = event.getPointerIdBits();
- final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
-
- // If for some reason we ended up in an inconsistent state where it looks like we
- // might produce a motion event with no pointers in it, then drop the event.
- if (newPointerIdBits == 0) {
- return false;
- }
+ // Calculate the number of pointers to deliver.
+ final int oldPointerIdBits = event.getPointerIdBits();
+ int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
- // If the number of pointers is the same and we don't need to perform any fancy
- // irreversible transformations, then we can reuse the motion event for this
- // dispatch as long as we are careful to revert any changes we make.
- // Otherwise we need to make a copy.
- final MotionEvent transformedEvent;
- if (newPointerIdBits == oldPointerIdBits) {
- if (child == null || child.hasIdentityMatrix()) {
- if (child == null) {
- handled = super.dispatchTouchEvent(event);
+ // If for some reason we ended up in an inconsistent state where it looks like we
+ // might produce a non-cancel motion event with no pointers in it, then drop the event.
+ // Make sure that we don't drop any cancel events.
+ if (newPointerIdBits == 0) {
+ if (event.getAction() != MotionEvent.ACTION_CANCEL) {
+ return false;
} else {
- final float offsetX = mScrollX - child.mLeft;
- final float offsetY = mScrollY - child.mTop;
- event.offsetLocation(offsetX, offsetY);
+ newPointerIdBits = oldPointerIdBits;
+ }
+ }
+
+ // If the number of pointers is the same and we don't need to perform any fancy
+ // irreversible transformations, then we can reuse the motion event for this
+ // dispatch as long as we are careful to revert any changes we make.
+ // Otherwise we need to make a copy.
+ final MotionEvent transformedEvent;
+ if (newPointerIdBits == oldPointerIdBits) {
+ if (child == null || child.hasIdentityMatrix()) {
+ if (child == null) {
+ handled = super.dispatchTouchEvent(event);
+ } else {
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+ event.offsetLocation(offsetX, offsetY);
- handled = child.dispatchTouchEvent(event);
+ handled = child.dispatchTouchEvent(event);
- event.offsetLocation(-offsetX, -offsetY);
+ event.offsetLocation(-offsetX, -offsetY);
+ }
+ return handled;
}
- return handled;
+ transformedEvent = MotionEvent.obtain(event);
+ } else {
+ transformedEvent = event.split(newPointerIdBits);
}
- transformedEvent = MotionEvent.obtain(event);
- } else {
- transformedEvent = event.split(newPointerIdBits);
- }
- // Perform any necessary transformations and dispatch.
- if (child == null) {
- handled = super.dispatchTouchEvent(transformedEvent);
- } else {
- final float offsetX = mScrollX - child.mLeft;
- final float offsetY = mScrollY - child.mTop;
- transformedEvent.offsetLocation(offsetX, offsetY);
- if (! child.hasIdentityMatrix()) {
- transformedEvent.transform(child.getInverseMatrix());
+ // Perform any necessary transformations and dispatch.
+ if (child == null) {
+ handled = super.dispatchTouchEvent(transformedEvent);
+ } else {
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+ transformedEvent.offsetLocation(offsetX, offsetY);
+ if (!child.hasIdentityMatrix()) {
+ transformedEvent.transform(child.getInverseMatrix());
+ }
+
+ handled = child.dispatchTouchEvent(transformedEvent);
}
- handled = child.dispatchTouchEvent(transformedEvent);
- }
+ // Done.
+ transformedEvent.recycle();
+ return handled;
- // Done.
- transformedEvent.recycle();
- return handled;
+ } finally {
+ event.setAction(oldAction);
+ }
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0e0262715d2fb97cc966b933ae34441ce7c0f9c2..e10cc28d0745dd0577ed7aa6d77f5734752dae82 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4345,6 +4345,7 @@ public final class ViewRootImpl implements ViewParent,
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, "view not visible");
+ mHasPendingTransactions = false;
} else if (cancelAndRedraw) {
if (!mWasLastDrawCanceled) {
logAndTrace("Canceling draw."
@@ -4372,6 +4373,7 @@ public final class ViewRootImpl implements ViewParent,
if (!performDraw(mActiveSurfaceSyncGroup)) {
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, mLastPerformDrawSkippedReason);
+ mHasPendingTransactions = false;
}
}
mWasLastDrawCanceled = cancelAndRedraw;
@@ -5312,6 +5314,7 @@ public final class ViewRootImpl implements ViewParent,
private void registerCallbackForPendingTransactions() {
Transaction t = new Transaction();
t.merge(mPendingTransaction);
+ mHasPendingTransactions = false;
registerRtFrameCallback(new FrameDrawingCallback() {
@Override
@@ -5391,6 +5394,7 @@ public final class ViewRootImpl implements ViewParent,
if (!usingAsyncReport && mHasPendingTransactions) {
pendingTransaction = new Transaction();
pendingTransaction.merge(mPendingTransaction);
+ mHasPendingTransactions = false;
} else {
pendingTransaction = null;
}
@@ -6101,6 +6105,12 @@ public final class ViewRootImpl implements ViewParent,
}
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
+ if (mImeBackAnimationController.isAnimationInProgress()) {
+ // IME predictive back animation is currently in progress which means that scrollY is
+ // currently controlled by ImeBackAnimationController.
+ return false;
+ }
+
final Rect ci = mAttachInfo.mContentInsets;
final Rect vi = mAttachInfo.mVisibleInsets;
int scrollY = 0;
@@ -9943,6 +9953,7 @@ public final class ViewRootImpl implements ViewParent,
}
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, "shutting down VRI");
+ mHasPendingTransactions = false;
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -12602,6 +12613,7 @@ public final class ViewRootImpl implements ViewParent,
if (mHasPendingTransactions) {
t = new Transaction();
t.merge(mPendingTransaction);
+ mHasPendingTransactions = false;
} else {
t = null;
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a4cea3364998dffedfa26edb7253b030535614f8..a87e5c8e1b567b68e9a563b56e71d7abaf0c778c 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1050,6 +1050,52 @@ public final class AccessibilityManager {
}
}
+ /**
+ * Registers callback for when user initialization has completed.
+ * Does nothing if the same callback is already registered.
+ *
+ * @param callback The callback to be registered
+ * @hide
+ */
+ public void registerUserInitializationCompleteCallback(
+ @NonNull IUserInitializationCompleteCallback callback) {
+ IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.registerUserInitializationCompleteCallback(callback);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while registering userInitializationCompleteCallback. ", re);
+ }
+ }
+
+ /**
+ * Unregisters callback for when user initialization has completed.
+ *
+ * @param callback The callback to be unregistered
+ * @hide
+ */
+ public void unregisterUserInitializationCompleteCallback(
+ @NonNull IUserInitializationCompleteCallback callback) {
+ IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.unregisterUserInitializationCompleteCallback(callback);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG,
+ "Error while unregistering userInitializationCompleteCallback. ", re);
+ }
+ }
+
/**
* Whether the current accessibility request comes from an
* {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
@@ -2049,9 +2095,7 @@ public final class AccessibilityManager {
* {@link android.view.Display#DEFAULT_DISPLAY}, is or lower than
* {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed.
*
- * @throws SecurityException if the app does not hold the
- * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
- * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
+ * @throws SecurityException if the app does not hold the required permissions.
*
* @hide
*/
@@ -2079,9 +2123,7 @@ public final class AccessibilityManager {
*
* @return {@code true} if the proxy is successfully unregistered.
*
- * @throws SecurityException if the app does not hold the
- * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
- * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
+ * @throws SecurityException if the app does not hold the required permissions.
*
* @hide
*/
@@ -2134,8 +2176,8 @@ public final class AccessibilityManager {
try {
return service.startFlashNotificationSequence(context.getOpPackageName(),
reason, mBinder);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while start flash notification sequence", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while start flash notification sequence", e);
return false;
}
}
@@ -2164,8 +2206,8 @@ public final class AccessibilityManager {
try {
return service.stopFlashNotificationSequence(context.getOpPackageName());
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while stop flash notification sequence", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while stop flash notification sequence", e);
return false;
}
}
@@ -2192,8 +2234,8 @@ public final class AccessibilityManager {
try {
return service.startFlashNotificationEvent(context.getOpPackageName(),
reason, reasonPkg);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while start flash notification event", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while start flash notification event", e);
return false;
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 72a1fe424906aa637b683215c08a782024925e3d..2de3ce8532e3be89e53dfb8c4ce4cf1fedec1e1b 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -29,6 +29,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.AccessibilityWindowAttributes;
import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import android.view.InputEvent;
import android.view.IWindow;
import android.view.MagnificationSpec;
@@ -156,13 +157,13 @@ interface IAccessibilityManager {
@EnforcePermission("INJECT_EVENTS")
void injectInputEventToInputFilter(in InputEvent event);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean startFlashNotificationSequence(String opPkg, int reason, IBinder token);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean stopFlashNotificationSequence(String opPkg);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean startFlashNotificationEvent(String opPkg, int reason, String reasonPkg);
@RequiresNoPermission
@@ -192,4 +193,10 @@ interface IAccessibilityManager {
@EnforcePermission("MANAGE_ACCESSIBILITY")
Bundle getA11yFeatureToTileMap(int userId);
+
+ @RequiresNoPermission
+ void registerUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback);
+
+ @RequiresNoPermission
+ void unregisterUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback);
}
diff --git a/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl
new file mode 100644
index 0000000000000000000000000000000000000000..fe6c8e25dd001b18ba36b79f123b10c29b7f66c1
--- /dev/null
+++ b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+/**
+ * A callback for when a new user finishes initializing
+ * NOTE: Must remain a oneway interface, as it is called from system_server while holding a lock.
+ * oneway allows it to return immediately and not hold the lock for longer than is necessary.
+ * @hide
+ */
+
+oneway interface IUserInitializationCompleteCallback {
+
+ /**
+ * Called when a user initialization completes.
+ *
+ * @param userId the id of the initialized user
+ */
+ @RequiresNoPermission
+ void onUserInitializationComplete(int userId);
+}
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index ed2bf79c51f47417e631b8c4c41801b4df516238..513587eacfa72566890cd8387c23152ac35c926f 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -18,6 +18,13 @@ flag {
bug: "297554934"
}
+flag {
+ name: "a11y_selection_api"
+ namespace: "accessibility"
+ description: "Enables new APIs for an AccessibilityService to control selection across nodes."
+ bug: "362782866"
+}
+
flag {
namespace: "accessibility"
name: "allow_shortcut_chooser_on_lockscreen"
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index 338037f705e435bc08ec28f039991a5d9e23185b..e9c85684dbd87442fc3a30824fc6f5ae64687175 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -14,4 +14,11 @@ flag {
name: "use_view_based_rotary_encoder_scroll_haptics"
description: "If enabled, the rotary encoder scroll haptic implementation in the View class will be used, and the HapticScrollFeedbackProvider logic for rotary encoder haptic will be muted."
bug: "299587011"
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "toolkit"
+ name: "enable_touch_scroll_feedback"
+ description: "Enables touchscreen haptic scroll feedback"
+ bug: "331830899"
+}
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 2f515fe7738cbca26970e26514d38c49f6a46a7b..3a008aad59bf473f39d2f3c67c17075e91c94647 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -323,14 +323,14 @@ final class IInputMethodManagerGlobalInvoker {
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, @Nullable ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
return service.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
- resultReceiver, reason);
+ resultReceiver, reason, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -339,14 +339,15 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
+ boolean async) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
return service.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
- reason);
+ reason, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -407,7 +408,7 @@ final class IInputMethodManagerGlobalInvoker {
@Nullable IRemoteInputConnection remoteInputConnection,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean useAsyncShowHideMethod) {
final IInputMethodManager service = getService();
if (service == null) {
return -1;
@@ -416,7 +417,7 @@ final class IInputMethodManagerGlobalInvoker {
service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
- imeDispatcher, advanceAngGetStartInputSequenceNumber());
+ imeDispatcher, advanceAngGetStartInputSequenceNumber(), useAsyncShowHideMethod);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 23d7732e469d6acaa20596a46c6f10ce1b965fb6..2f649c21fe08041f14030e8a808f5d476e32be62 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -56,6 +56,7 @@ import android.annotation.UiThread;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.app.PropertyInvalidatedCache;
+import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -440,6 +441,36 @@ public final class InputMethodManager {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id.
+ /**
+ * Use async method for {@link InputMethodManager#showSoftInput(View, int, ResultReceiver)},
+ * {@link InputMethodManager#showSoftInput(View, int)} and
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int, ResultReceiver)},
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} for apps targeting V+.
+ *
+ * Apps can incorrectly rely on {@link InputMethodManager#showSoftInput(View, int)} and
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} method return type
+ * to interpret result of a request rather than relying on {@link ResultReceiver}. The return
+ * type of the method was never documented to have accurate info of visibility but few apps
+ * incorrectly rely on it.
+ *
+ * Starting Android V, we use async calls into system_server which returns {@code true} if
+ * method call was made but return type doesn't guarantee execution.
+ * Apps targeting older versions will fallback to existing behavior of calling synchronous
+ * methods which had undocumented result in return type.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final long USE_ASYNC_SHOW_HIDE_METHOD = 352594277L; // This is a bug id.
+
+ /**
+ * Version-gating is guarded by bug-fix flag.
+ */
+ private static final boolean ASYNC_SHOW_HIDE_METHOD_ENABLED =
+ !Flags.compatchangeForZerojankproxy()
+ || CompatChanges.isChangeEnabled(USE_ASYNC_SHOW_HIDE_METHOD);
+
/**
* If {@code true}, avoid calling the
* {@link com.android.server.inputmethod.InputMethodManagerService InputMethodManagerService}
@@ -2246,6 +2277,8 @@ public final class InputMethodManager {
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags) {
// Re-dispatch if there is a context mismatch.
@@ -2315,6 +2348,8 @@ public final class InputMethodManager {
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
@@ -2383,7 +2418,8 @@ public final class InputMethodManager {
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- reason);
+ reason,
+ ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
}
@@ -2426,7 +2462,8 @@ public final class InputMethodManager {
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- reason);
+ reason,
+ ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
@@ -2459,6 +2496,9 @@ public final class InputMethodManager {
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@link ResultReceiver} in
+ * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
@@ -2487,6 +2527,8 @@ public final class InputMethodManager {
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver) {
@@ -2530,7 +2572,7 @@ public final class InputMethodManager {
return true;
} else {
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken,
- statsToken, flags, resultReceiver, reason);
+ statsToken, flags, resultReceiver, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
}
@@ -2573,7 +2615,7 @@ public final class InputMethodManager {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, view.getWindowToken(),
- statsToken, flags, null, reason);
+ statsToken, flags, null, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
@@ -3350,7 +3392,7 @@ public final class InputMethodManager {
servedInputConnection == null ? null
: servedInputConnection.asIRemoteAccessibilityInputConnection(),
view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
- mImeDispatcher);
+ mImeDispatcher, ASYNC_SHOW_HIDE_METHOD_ENABLED);
} else {
res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
@@ -3653,7 +3695,8 @@ public final class InputMethodManager {
statsToken,
HIDE_NOT_ALWAYS,
null,
- reason);
+ reason,
+ true /*async */);
}
}
@@ -3745,7 +3788,7 @@ public final class InputMethodManager {
IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken,
0 /* flags */, null /* resultReceiver */,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, true /* async */);
}
}
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index e294ee2d3a91e030c4210049cc83dca20c9b2c96..bae8affcec473f353e70c676a9863531cedf3584 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -136,3 +136,14 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "compatchange_for_zerojankproxy"
+ namespace: "input_method"
+ description: "Version-gate the sync/async nature of IMM#show/hideSoftInput() when using zeroJankProxy."
+ bug: "352594277"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f336b5d8a727458a0fc3bc148b3cf5cca7ac4033..ffe8c80023c41eb45f358cd15f157746ad428aec 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3088,11 +3088,11 @@ public class WebView extends AbsoluteLayout
}
if (Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager == null) {
+ if (WebViewFactory.isWebViewSupported()) {
+ return WebViewUpdateManager.getInstance().getCurrentWebViewPackage();
+ } else {
return null;
}
- return manager.getCurrentWebViewPackage();
} else {
IWebViewUpdateService service = WebViewFactory.getUpdateService();
if (service == null) {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index c53a0e158deaf47a8ea4b4e789f79401c2ce2fc0..e10a3983f0b099776f983c88591007a68e013de9 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -208,7 +208,7 @@ public final class WebViewFactory {
public MissingWebViewPackageException(Exception e) { super(e); }
}
- private static boolean isWebViewSupported() {
+ static boolean isWebViewSupported() {
// No lock; this is a benign race as Boolean's state is final and the PackageManager call
// will always return the same value.
if (sWebViewSupported == null) {
@@ -318,7 +318,7 @@ public final class WebViewFactory {
String libraryFileName;
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
- PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+ PackageManager.GET_META_DATA);
libraryFileName = getWebViewLibrary(packageInfo.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOGTAG, "Couldn't find package " + packageName);
@@ -479,7 +479,6 @@ public final class WebViewFactory {
newPackageInfo = pm.getPackageInfo(
response.packageInfo.packageName,
PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
// Make sure that we fetch the current provider even if its not
// installed for the current user
| PackageManager.MATCH_UNINSTALLED_PACKAGES
diff --git a/core/java/android/webkit/WebViewUpdateManager.java b/core/java/android/webkit/WebViewUpdateManager.java
index dd48df97590601839453add46b2f339e0318d753..0eb710015ea994ae503493ddcd0a35df53b63dab 100644
--- a/core/java/android/webkit/WebViewUpdateManager.java
+++ b/core/java/android/webkit/WebViewUpdateManager.java
@@ -19,12 +19,14 @@ package android.webkit;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
import android.content.Context;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
/** @hide */
@@ -43,8 +45,11 @@ public final class WebViewUpdateManager {
*
* This exists for the benefit of callsites without a {@link Context}; prefer
* {@link Context#getSystemService(Class)} otherwise.
+ *
+ * This can only be used on devices with {@link PackageManager#FEATURE_WEBVIEW}.
*/
@SuppressLint("ManagerLookup") // service opts in to getSystemServiceWithNoContext()
+ @RequiresFeature(PackageManager.FEATURE_WEBVIEW)
public static @Nullable WebViewUpdateManager getInstance() {
return (WebViewUpdateManager) SystemServiceRegistry.getSystemServiceWithNoContext(
Context.WEBVIEW_UPDATE_SERVICE);
diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java
index 6f53ddeafef1b49fe9be1f2b1be3f74538f3b39b..01af182a10fa0862701dd2c6183732dfb7d9c556 100644
--- a/core/java/android/webkit/WebViewUpdateService.java
+++ b/core/java/android/webkit/WebViewUpdateService.java
@@ -34,11 +34,11 @@ public final class WebViewUpdateService {
*/
public static WebViewProviderInfo[] getAllWebViewPackages() {
if (Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager == null) {
+ if (WebViewFactory.isWebViewSupported()) {
+ return WebViewUpdateManager.getInstance().getAllWebViewPackages();
+ } else {
return new WebViewProviderInfo[0];
}
- return manager.getAllWebViewPackages();
} else {
IWebViewUpdateService service = getUpdateService();
if (service == null) {
@@ -57,11 +57,11 @@ public final class WebViewUpdateService {
*/
public static WebViewProviderInfo[] getValidWebViewPackages() {
if (Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager == null) {
+ if (WebViewFactory.isWebViewSupported()) {
+ return WebViewUpdateManager.getInstance().getValidWebViewPackages();
+ } else {
return new WebViewProviderInfo[0];
}
- return manager.getValidWebViewPackages();
} else {
IWebViewUpdateService service = getUpdateService();
if (service == null) {
@@ -80,11 +80,11 @@ public final class WebViewUpdateService {
*/
public static String getCurrentWebViewPackageName() {
if (Flags.updateServiceIpcWrapper()) {
- WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
- if (manager == null) {
+ if (WebViewFactory.isWebViewSupported()) {
+ return WebViewUpdateManager.getInstance().getCurrentWebViewPackageName();
+ } else {
return null;
}
- return manager.getCurrentWebViewPackageName();
} else {
IWebViewUpdateService service = getUpdateService();
if (service == null) {
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index defe61e506af4ac444c95ce26dbd97bd531415ab..b21a490cc506f353f9a1df496018725b2c10f528 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -9,3 +9,12 @@ flag {
bug: "319292658"
is_fixed_read_only: true
}
+
+flag {
+ name: "mainline_apis"
+ is_exported: true
+ namespace: "webview"
+ description: "New APIs required by WebViewBootstrap mainline module"
+ bug: "310653407"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ab6b5122ea0560fdb90912667d8a5c48b069e735..3c854ea4fad4a089173e0fada4a5a2d24ec805fe 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.flags.Flags.enableTouchScrollFeedback;
+import static android.view.flags.Flags.scrollFeedbackApi;
import static android.view.flags.Flags.viewVelocityApi;
import android.annotation.ColorInt;
@@ -82,7 +84,6 @@ import android.view.animation.LinearInterpolator;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
-import android.view.flags.Flags;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -3703,7 +3704,6 @@ public abstract class AbsListView extends AdapterView implements Te
// If it's non-null, we're already in a scroll.
mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");
}
-
if (y != mLastY) {
// We may be here after stopping a fling and continuing to scroll.
// If so, we haven't disallowed intercepting touch events yet.
@@ -3735,8 +3735,15 @@ public abstract class AbsListView extends AdapterView implements Te
boolean atEdge = false;
if (incrementalDeltaY != 0) {
atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
- }
+ // TODO: b/360198915 - Add unit testing for using ScrollFeedbackProvider
+ if (enableTouchScrollFeedback()) {
+ initHapticScrollFeedbackProviderIfNotExists();
+ mHapticScrollFeedbackProvider.onScrollProgress(
+ vtev.getDeviceId(), vtev.getSource(), MotionEvent.AXIS_Y,
+ incrementalDeltaY);
+ }
+ }
// Check to see if we have bumped into the scroll limit
motionView = this.getChildAt(motionIndex);
if (motionView != null) {
@@ -3745,7 +3752,6 @@ public abstract class AbsListView extends AdapterView implements Te
final int motionViewRealTop = motionView.getTop();
if (atEdge) {
// Apply overscroll
-
int overscroll = -incrementalDeltaY -
(motionViewRealTop - motionViewPrevTop);
if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
@@ -3772,6 +3778,15 @@ public abstract class AbsListView extends AdapterView implements Te
mDirection = 0; // Reset when entering overscroll.
mTouchMode = TOUCH_MODE_OVERSCROLL;
}
+
+ if (enableTouchScrollFeedback()) {
+ initHapticScrollFeedbackProviderIfNotExists();
+ mHapticScrollFeedbackProvider.onScrollLimit(
+ vtev.getDeviceId(), vtev.getSource(),
+ MotionEvent.AXIS_Y,
+ /* isStart= */ incrementalDeltaY > 0);
+ }
+
if (incrementalDeltaY > 0) {
mEdgeGlowTop.onPullDistance((float) -overscroll / getHeight(),
(float) x / getWidth());
@@ -3981,7 +3996,6 @@ public abstract class AbsListView extends AdapterView implements Te
if (mFastScroll != null && mFastScroll.onTouchEvent(ev)) {
return true;
}
-
initVelocityTrackerIfNotExists();
final MotionEvent vtev = MotionEvent.obtain(ev);
@@ -4520,7 +4534,7 @@ public abstract class AbsListView extends AdapterView implements Te
final int overscrollMode = getOverScrollMode();
if (!trackMotionScroll(delta, delta)) {
- if (Flags.scrollFeedbackApi()) {
+ if (scrollFeedbackApi()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollProgress(
event.getDeviceId(), event.getSource(), axis, delta);
@@ -4536,7 +4550,7 @@ public abstract class AbsListView extends AdapterView implements Te
float overscroll = (delta - (motionViewRealTop - motionViewPrevTop))
/ ((float) getHeight());
boolean hitTopLimit = delta > 0;
- if (Flags.scrollFeedbackApi()) {
+ if (scrollFeedbackApi()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollLimit(
event.getDeviceId(), event.getSource(), axis,
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index 9931aea41913662c652f3a3577d6655cfb150af6..ac5656dd0201af903d0774ca96323115c56692d7 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -289,8 +289,7 @@ public class Chronometer extends TextView {
private synchronized void updateText(long now) {
mNow = now;
- long seconds = mCountDown ? mBase - now : now - mBase;
- seconds /= 1000;
+ long seconds = Math.round((mCountDown ? mBase - now - 499 : now - mBase) / 1000f);
boolean negative = false;
if (seconds < 0) {
seconds = -seconds;
@@ -348,9 +347,19 @@ public class Chronometer extends TextView {
};
private void postTickOnNextSecond() {
- long nowMillis = SystemClock.elapsedRealtime();
- int millis = (int) ((nowMillis - mBase) % 1000);
- postDelayed(mTickRunnable, 1000 - millis);
+ long nowMillis = mNow;
+ long delayMillis;
+ if (mCountDown) {
+ delayMillis = (mBase - nowMillis) % 1000;
+ if (delayMillis <= 0) {
+ delayMillis += 1000;
+ }
+ } else {
+ delayMillis = 1000 - (Math.abs(nowMillis - mBase) % 1000);
+ }
+ // Aim for 1 millisecond into the next second so we don't update exactly on the second
+ delayMillis++;
+ postDelayed(mTickRunnable, delayMillis);
}
void dispatchChronometerTick() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 03a26722da8f2c342f6f59d284ad14252738316c..0acc6bde5bfd78e6c83a302d2ba54639c3fcefae 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -20,6 +20,7 @@ import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
import static com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect;
+import static com.android.text.flags.Flags.contextMenuHideUnavailableItems;
import android.R;
import android.animation.ValueAnimator;
@@ -3250,62 +3251,135 @@ public class Editor {
final int menuItemOrderShare = 9;
final int menuItemOrderAutofill = 10;
- menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
- com.android.internal.R.string.undo)
- .setAlphabeticShortcut('z')
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
- .setIcon(a.getDrawable(0))
- .setEnabled(mTextView.canUndo());
- menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
- com.android.internal.R.string.redo)
- .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
- .setIcon(a.getDrawable(1))
- .setEnabled(mTextView.canRedo());
-
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
- com.android.internal.R.string.cut)
- .setAlphabeticShortcut('x')
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
- .setIcon(a.getDrawable(2))
- .setEnabled(mTextView.canCut());
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
- com.android.internal.R.string.copy)
- .setAlphabeticShortcut('c')
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
- .setIcon(a.getDrawable(3))
- .setEnabled(mTextView.canCopy());
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
- com.android.internal.R.string.paste)
- .setAlphabeticShortcut('v')
- .setEnabled(mTextView.canPaste())
- .setIcon(a.getDrawable(4))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
- menuItemOrderPasteAsPlainText,
- com.android.internal.R.string.paste_as_plain_text)
- .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
- .setEnabled(mTextView.canPasteAsPlainText())
- .setIcon(a.getDrawable(4))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
- menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
- .setAlphabeticShortcut('a')
- .setEnabled(mTextView.canSelectAllText())
- .setIcon(a.getDrawable(5))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
-
- menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
- com.android.internal.R.string.share)
- .setEnabled(mTextView.canShare())
- .setIcon(a.getDrawable(6))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
- final String selected = mTextView.getSelectedText();
- menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
- android.R.string.autofill)
- .setEnabled(mTextView.canRequestAutofill()
- && (selected == null || selected.isEmpty()))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ if (contextMenuHideUnavailableItems()) {
+ if (mTextView.canUndo()) {
+ menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
+ com.android.internal.R.string.undo)
+ .setAlphabeticShortcut('z')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(0));
+ }
+
+ if (mTextView.canRedo()) {
+ menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
+ com.android.internal.R.string.redo)
+ .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(1));
+ }
+
+ if (mTextView.canCut()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
+ com.android.internal.R.string.cut)
+ .setAlphabeticShortcut('x')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(2));
+ }
+
+ if (mTextView.canCopy()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
+ com.android.internal.R.string.copy)
+ .setAlphabeticShortcut('c')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(3));
+ }
+
+ if (mTextView.canPaste()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
+ com.android.internal.R.string.paste)
+ .setAlphabeticShortcut('v')
+ .setIcon(a.getDrawable(4))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+
+ if (mTextView.canPasteAsPlainText()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
+ menuItemOrderPasteAsPlainText,
+ com.android.internal.R.string.paste_as_plain_text)
+ .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+ .setIcon(a.getDrawable(4))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+
+ if (mTextView.canSelectAllText()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
+ menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
+ .setAlphabeticShortcut('a')
+ .setIcon(a.getDrawable(5))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+
+ if (mTextView.canShare()) {
+ menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
+ com.android.internal.R.string.share)
+ .setIcon(a.getDrawable(6))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+
+ final String selected = mTextView.getSelectedText();
+ if (mTextView.canRequestAutofill() && (selected == null || selected.isEmpty())) {
+ menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
+ android.R.string.autofill)
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+ } else {
+ menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
+ com.android.internal.R.string.undo)
+ .setAlphabeticShortcut('z')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(0))
+ .setEnabled(mTextView.canUndo());
+ menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
+ com.android.internal.R.string.redo)
+ .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(1))
+ .setEnabled(mTextView.canRedo());
+
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
+ com.android.internal.R.string.cut)
+ .setAlphabeticShortcut('x')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(2))
+ .setEnabled(mTextView.canCut());
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
+ com.android.internal.R.string.copy)
+ .setAlphabeticShortcut('c')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(3))
+ .setEnabled(mTextView.canCopy());
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
+ com.android.internal.R.string.paste)
+ .setAlphabeticShortcut('v')
+ .setEnabled(mTextView.canPaste())
+ .setIcon(a.getDrawable(4))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
+ menuItemOrderPasteAsPlainText,
+ com.android.internal.R.string.paste_as_plain_text)
+ .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+ .setEnabled(mTextView.canPasteAsPlainText())
+ .setIcon(a.getDrawable(4))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
+ menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
+ .setAlphabeticShortcut('a')
+ .setEnabled(mTextView.canSelectAllText())
+ .setIcon(a.getDrawable(5))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+
+ menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
+ com.android.internal.R.string.share)
+ .setEnabled(mTextView.canShare())
+ .setIcon(a.getDrawable(6))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ final String selected = mTextView.getSelectedText();
+ menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
+ android.R.string.autofill)
+ .setEnabled(mTextView.canRequestAutofill()
+ && (selected == null || selected.isEmpty()))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
a.recycle();
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index eb35817176373d8f02f18890faa293d5b595b533..a395c1a05744bcb43c6fef83e30145678da8f92e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1446,6 +1446,11 @@ public class RemoteViews implements Parcelable, Filter {
result.complete(items);
}
+ @Override
+ public void onNullBinding(ComponentName name) {
+ context.unbindService(this);
+ }
+
@Override
public void onServiceDisconnected(ComponentName componentName) { }
});
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 2f28a8704cd3d5d2e18b08d91fa8b6bf363d6efd..118edc29f37807cd56465b728e4150474699c2f0 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -240,6 +240,11 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
}
}
+ @Override
+ public void onNullBinding(ComponentName name) {
+ enqueueDeferredUnbindServiceMessage();
+ }
+
@Override
public void handleMessage(Message msg) {
RemoteViewsAdapter adapter = mAdapter.get();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1ea20fa85bd4d77e6d9048f5d3d9b5c1315d6223..ef941da0e32dd1d120b21be71248c2d5192e0f70 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1659,11 +1659,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (!hasUseBoundForWidthValue) {
- if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
- mUseBoundsForWidth = Flags.useBoundsForWidth();
- } else {
- mUseBoundsForWidth = false;
- }
+ mUseBoundsForWidth = CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH);
}
// TODO(b/179693024): Use a ChangeId instead.
@@ -14375,7 +14371,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Matrix matrix = mTempMatrix;
matrix.reset();
- transformMatrixToLocal(matrix);
+ transformMatrixRootToLocal(matrix);
editorBounds.set(rect);
// When the view has transformations like scaleX/scaleY computing the global visible
// rectangle will already apply the transformations. The getLocalVisibleRect only offsets
@@ -15552,15 +15548,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- boolean canUndo() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canUndo() {
return mEditor != null && mEditor.canUndo();
}
- boolean canRedo() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canRedo() {
return mEditor != null && mEditor.canRedo();
}
- boolean canCut() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canCut() {
if (hasPasswordTransformationMethod()) {
return false;
}
@@ -15573,7 +15575,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
- boolean canCopy() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canCopy() {
if (hasPasswordTransformationMethod()) {
return false;
}
@@ -15594,7 +15598,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
&& isSuggestionsEnabled() && mEditor.shouldOfferToShowSuggestions();
}
- boolean canShare() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canShare() {
if (!getContext().canStartActivityForResult() || !isDeviceProvisioned()
|| !getContext().getResources().getBoolean(
com.android.internal.R.bool.config_textShareSupported)) {
@@ -15613,8 +15619,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mDeviceProvisionedState == DEVICE_PROVISIONED_YES;
}
+ /** @hide */
+ @VisibleForTesting
@UnsupportedAppUsage
- boolean canPaste() {
+ public boolean canPaste() {
return (mText instanceof Editable
&& mEditor != null && mEditor.mKeyListener != null
&& getSelectionStart() >= 0
@@ -15622,7 +15630,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
&& getClipboardManagerForUser().hasPrimaryClip());
}
- boolean canPasteAsPlainText() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canPasteAsPlainText() {
if (!canPaste()) {
return false;
}
@@ -15644,7 +15654,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return canShare();
}
- boolean canSelectAllText() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canSelectAllText() {
return canSelectText() && !hasPasswordTransformationMethod()
&& !(getSelectionStart() == 0 && getSelectionEnd() == mText.length());
}
diff --git a/core/java/android/window/BackAnimationAdapter.java b/core/java/android/window/BackAnimationAdapter.java
index 5eb34e694a572b76dc93c5f7b4d37c7e3be99cfa..153e153331e288d34f02e2497baa9a29c18a7d0b 100644
--- a/core/java/android/window/BackAnimationAdapter.java
+++ b/core/java/android/window/BackAnimationAdapter.java
@@ -16,9 +16,12 @@
package android.window;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+
/**
* Object that describes how to run a remote back animation.
*
@@ -26,6 +29,7 @@ import android.os.Parcelable;
*/
public class BackAnimationAdapter implements Parcelable {
private final IBackAnimationRunner mRunner;
+ private int[] mSupportedAnimators;
public BackAnimationAdapter(IBackAnimationRunner runner) {
mRunner = runner;
@@ -33,12 +37,23 @@ public class BackAnimationAdapter implements Parcelable {
public BackAnimationAdapter(Parcel in) {
mRunner = IBackAnimationRunner.Stub.asInterface(in.readStrongBinder());
+ mSupportedAnimators = new int[in.readInt()];
+ in.readIntArray(mSupportedAnimators);
}
public IBackAnimationRunner getRunner() {
return mRunner;
}
+ /** Update the latest animators in the system. */
+ public void updateSupportedAnimators(@NonNull ArrayList animators) {
+ final int size = animators.size();
+ mSupportedAnimators = new int[size];
+ for (int i = size - 1; i >= 0; --i) {
+ mSupportedAnimators[i] = animators.get(i);
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -47,6 +62,8 @@ public class BackAnimationAdapter implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongInterface(mRunner);
+ dest.writeInt(mSupportedAnimators.length);
+ dest.writeIntArray(mSupportedAnimators);
}
public static final @android.annotation.NonNull Creator CREATOR =
@@ -59,4 +76,19 @@ public class BackAnimationAdapter implements Parcelable {
return new BackAnimationAdapter[size];
}
};
+
+ /**
+ * Check if the back type is animatable.
+ */
+ public boolean isAnimatable(@BackNavigationInfo.BackTargetType int backType) {
+ if (mSupportedAnimators == null) {
+ return false;
+ }
+ for (int i = mSupportedAnimators.length - 1; i >= 0; --i) {
+ if (backType == mSupportedAnimators[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 58b5757a47bb3de618c7096691110378c3f2d98a..b8a11cf063e2a837424dc02b092372bc5ca1128f 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -46,6 +46,19 @@ interface ITaskFragmentOrganizerController {
*/
void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
+ /**
+ * Registers remote animations per transition type for the organizer. It will override the
+ * animations if the transition only contains windows that belong to the organized
+ * TaskFragments in the given Task.
+ */
+ void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
+ in RemoteAnimationDefinition definition);
+
+ /**
+ * Unregisters remote animations per transition type for the organizer.
+ */
+ void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
+
/**
* Saves the state in the system, where the state can be restored if the process of
* the TaskFragmentOrganizer is restarted.
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 205f1defccd193498ac7c0a7e94508572a494017..9a7bce0c52ee0fe89580531666079c0b59613e08 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -59,7 +59,6 @@ import android.os.IBinder;
import android.util.Log;
import android.view.InsetsState;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -152,9 +151,11 @@ public class SnapshotDrawerUtils {
@VisibleForTesting
public void setFrames(Rect frame, Rect systemBarInsets) {
mFrame.set(frame);
- mSystemBarInsets.set(systemBarInsets);
mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH);
- mSystemBarBackgroundPainter.setInsets(systemBarInsets);
+ if (!Flags.drawSnapshotAspectRatioMatch() && systemBarInsets != null) {
+ mSystemBarInsets.set(systemBarInsets);
+ mSystemBarBackgroundPainter.setInsets(systemBarInsets);
+ }
}
private void drawSnapshot(boolean releaseAfterDraw) {
@@ -185,7 +186,6 @@ public class SnapshotDrawerUtils {
private void drawSizeMismatchSnapshot() {
final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
- final SurfaceSession session = new SurfaceSession();
// We consider nearly matched dimensions as there can be rounding errors and the user
// won't notice very minute differences from scaling one dimension more than the other
@@ -193,7 +193,7 @@ public class SnapshotDrawerUtils {
&& !Flags.drawSnapshotAspectRatioMatch();
// Keep a reference to it such that it doesn't get destroyed when finalized.
- SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
+ SurfaceControl childSurfaceControl = new SurfaceControl.Builder()
.setName(mTitle + " - task-snapshot-surface")
.setBLASTLayer()
.setFormat(buffer.getFormat())
@@ -396,9 +396,12 @@ public class SnapshotDrawerUtils {
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
final ActivityManager.TaskDescription taskDescription =
getOrCreateTaskDescription(runningTaskInfo);
- drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
- attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
- final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
+ Rect systemBarInsets = null;
+ if (!Flags.drawSnapshotAspectRatioMatch()) {
+ drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
+ attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
+ systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
+ }
drawSurface.setFrames(windowBounds, systemBarInsets);
drawSurface.drawSnapshot(releaseAfterDraw);
}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 027d323bfcd124df8dee8004c6a7be1811cc1cce..4cc0d8a77a2b8ccaac1addbb2dcf98bb87c50873 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -34,6 +34,7 @@ import android.app.Activity;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import com.android.window.flags.Flags;
@@ -224,6 +225,34 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
}
}
+ /**
+ * Registers remote animations per transition type for the organizer. It will override the
+ * animations if the transition only contains windows that belong to the organized
+ * TaskFragments, and at least one of the transition window is embedded (not filling the Task).
+ * @hide
+ */
+ @CallSuper
+ public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
+ try {
+ getController().registerRemoteAnimations(mInterface, definition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters remote animations per transition type for the organizer.
+ * @hide
+ */
+ @CallSuper
+ public void unregisterRemoteAnimations() {
+ try {
+ getController().unregisterRemoteAnimations(mInterface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Saves the state in the system, where the state can be restored if the process of
* the TaskFragmentOrganizer is restarted.
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index 15554167c70214c0311f799803d27ba14f1888b0..efd74c375b824e22d2aeca573b8ce799fdf971e7 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -41,6 +41,8 @@ public final class TaskFragmentParentInfo implements Parcelable {
private final int mDisplayId;
+ private final int mTaskId;
+
private final boolean mVisible;
private final boolean mHasDirectActivity;
@@ -49,9 +51,11 @@ public final class TaskFragmentParentInfo implements Parcelable {
/** @hide */
public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
- boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) {
+ int taskId, boolean visible, boolean hasDirectActivity,
+ @Nullable SurfaceControl decorSurface) {
mConfiguration.setTo(configuration);
mDisplayId = displayId;
+ mTaskId = taskId;
mVisible = visible;
mHasDirectActivity = hasDirectActivity;
mDecorSurface = decorSurface;
@@ -61,6 +65,7 @@ public final class TaskFragmentParentInfo implements Parcelable {
public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
mConfiguration.setTo(info.getConfiguration());
mDisplayId = info.mDisplayId;
+ mTaskId = info.mTaskId;
mVisible = info.mVisible;
mHasDirectActivity = info.mHasDirectActivity;
mDecorSurface = info.mDecorSurface;
@@ -86,6 +91,15 @@ public final class TaskFragmentParentInfo implements Parcelable {
return mDisplayId;
}
+ /**
+ * The id of the parent Task.
+ *
+ * @hide
+ */
+ public int getTaskId() {
+ return mTaskId;
+ }
+
/**
* Whether the parent Task is visible or not
*
@@ -120,7 +134,8 @@ public final class TaskFragmentParentInfo implements Parcelable {
return false;
}
return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
- && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity
+ && mTaskId == that.mTaskId && mVisible == that.mVisible
+ && mHasDirectActivity == that.mHasDirectActivity
&& mDecorSurface == that.mDecorSurface;
}
@@ -140,6 +155,7 @@ public final class TaskFragmentParentInfo implements Parcelable {
return TaskFragmentParentInfo.class.getSimpleName() + ":{"
+ "config=" + mConfiguration
+ ", displayId=" + mDisplayId
+ + ", taskId=" + mTaskId
+ ", visible=" + mVisible
+ ", hasDirectActivity=" + mHasDirectActivity
+ ", decorSurface=" + mDecorSurface
@@ -163,6 +179,7 @@ public final class TaskFragmentParentInfo implements Parcelable {
final TaskFragmentParentInfo that = (TaskFragmentParentInfo) obj;
return mConfiguration.equals(that.mConfiguration)
&& mDisplayId == that.mDisplayId
+ && mTaskId == that.mTaskId
&& mVisible == that.mVisible
&& mHasDirectActivity == that.mHasDirectActivity
&& mDecorSurface == that.mDecorSurface;
@@ -172,6 +189,7 @@ public final class TaskFragmentParentInfo implements Parcelable {
public int hashCode() {
int result = mConfiguration.hashCode();
result = 31 * result + mDisplayId;
+ result = 31 * result + mTaskId;
result = 31 * result + (mVisible ? 1 : 0);
result = 31 * result + (mHasDirectActivity ? 1 : 0);
result = 31 * result + Objects.hashCode(mDecorSurface);
@@ -183,6 +201,7 @@ public final class TaskFragmentParentInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
mConfiguration.writeToParcel(dest, flags);
dest.writeInt(mDisplayId);
+ dest.writeInt(mTaskId);
dest.writeBoolean(mVisible);
dest.writeBoolean(mHasDirectActivity);
dest.writeTypedObject(mDecorSurface, flags);
@@ -191,6 +210,7 @@ public final class TaskFragmentParentInfo implements Parcelable {
private TaskFragmentParentInfo(Parcel in) {
mConfiguration.readFromParcel(in);
mDisplayId = in.readInt();
+ mTaskId = in.readInt();
mVisible = in.readBoolean();
mHasDirectActivity = in.readBoolean();
mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR);
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 217bca77af0c229d5f48e5e0a317990b523230fc..3998ac634f4341a56a8a2fc6b1a0b6eefbf98feb 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -1,14 +1,6 @@
package: "com.android.window.flags"
container: "system"
-flag {
- name: "enable_scaled_resizing"
- namespace: "lse_desktop_experience"
- description: "Enable the resizing of un-resizable apps through scaling their bounds up/down"
- bug: "320350734"
- is_fixed_read_only: true
-}
-
flag {
name: "enable_desktop_windowing_mode"
namespace: "lse_desktop_experience"
@@ -30,6 +22,13 @@ flag {
bug: "324377962"
}
+flag {
+ name: "enable_windowing_scaled_resizing"
+ namespace: "lse_desktop_experience"
+ description: "Enables the resizing of non-resizable apps through scaling their bounds up/down"
+ bug: "319844447"
+}
+
flag {
name: "disable_non_resizable_app_snap_resizing"
namespace: "lse_desktop_experience"
@@ -114,6 +113,16 @@ flag {
bug: "325240051"
}
+flag {
+ name: "respect_orientation_change_for_unresizeable"
+ namespace: "lse_desktop_experience"
+ description: "Whether to resize task to respect requested orientation change of unresizeable activity"
+ bug: "353338503"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
flag {
name: "enable_camera_compat_for_desktop_windowing"
namespace: "lse_desktop_experience"
@@ -235,3 +244,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_desktop_windowing_persistence"
+ namespace: "lse_desktop_experience"
+ description: "Persists the desktop windowing session across reboots."
+ bug: "350456942"
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 6ce9725f95b09f185dacd0d098bf80f93b9c66ed..cd31850b281c582efa0c49b1a311f6f39e271be6 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -71,3 +71,11 @@ flag {
bug: "339720406"
}
+flag {
+ name: "bal_reduce_grace_period"
+ namespace: "responsible_apis"
+ description: "Changes to reduce or ideally remove the grace period exemption."
+ bug: "362575865"
+}
+
+
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index 8c6721a7e96ab7aa219eee549d91759c265a13d1..efacc346ac0abdc8237f911c1df05eaa484754af 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -49,3 +49,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "avoid_rebinding_intentionally_disconnected_wallpaper"
+ namespace: "systemui"
+ description: "Prevents rebinding with intentionally disconnected wallpaper services."
+ bug: "332871851"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 67fc27042ff8848952629d9089421266f23263d1..a786fc24d9a7991e8eb0cff26cdaf21b34e84b48 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -246,3 +246,14 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "always_capture_activity_snapshot"
+ namespace: "windowing_frontend"
+ description: "Always capture activity snapshot regardless predictive back status"
+ bug: "362183912"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 1c26687c97e975ecd56a57d6f5c4bd879dae04f7..2e0ff3db6c5073e223708a15f9e501152fc6d74e 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -34,6 +34,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
@@ -176,24 +177,19 @@ public final class ShortcutUtils {
* @param type The shortcut type.
* @return Mapping key in Settings.
*/
+ @SuppressLint("SwitchIntDef")
public static String convertToKey(@UserShortcutType int type) {
- switch (type) {
- case SOFTWARE:
- return Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
- case GESTURE:
- return Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS;
- case HARDWARE:
- return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
- case TRIPLETAP:
- return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
- case TWOFINGER_DOUBLETAP:
- return Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
- case QUICK_SETTINGS:
- return Settings.Secure.ACCESSIBILITY_QS_TARGETS;
- default:
- throw new IllegalArgumentException(
- "Unsupported user shortcut type: " + type);
- }
+ return switch (type) {
+ case SOFTWARE -> Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
+ case GESTURE -> Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS;
+ case HARDWARE -> Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+ case TRIPLETAP -> Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
+ case TWOFINGER_DOUBLETAP ->
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
+ case QUICK_SETTINGS -> Settings.Secure.ACCESSIBILITY_QS_TARGETS;
+ default -> throw new IllegalArgumentException(
+ "Unsupported user shortcut type: " + type);
+ };
}
/**
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index ce17d788f93b47413c63cd362d7d25cb1385f742..ef4acd1cdfcb0b1269c1b84bf7b96ec9aed9d99a 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.os.Bundle;
import android.os.LocaleList;
import android.text.TextUtils;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -265,6 +266,11 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
public void onListItemClick(ListView parent, View v, int position, long id) {
final LocaleStore.LocaleInfo locale =
(LocaleStore.LocaleInfo) parent.getAdapter().getItem(position);
+ if (locale == null) {
+ Log.d(TAG, "Can not get the locale.");
+ return;
+ }
+
// Special case for resetting the app locale to equal the system locale.
boolean isSystemLocale = locale.isSystemLocale();
boolean isRegionLocale = locale.getParent() != null;
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 9f5ed65fa2523da65698bc172186d43b7a38958c..21fbf9d03c7118b993d36cbbc1065eecda38c832 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -134,7 +134,8 @@ public class BrightnessSynchronizer {
* Prints data on dumpsys.
*/
public void dump(PrintWriter pw) {
- pw.println("BrightnessSynchronizer");
+ pw.println("BrightnessSynchronizer:");
+ pw.println("-----------------------");
pw.println(" mLatestIntBrightness=" + mLatestIntBrightness);
pw.println(" mLatestFloatBrightness=" + mLatestFloatBrightness);
pw.println(" mCurrentUpdate=" + mCurrentUpdate);
diff --git a/core/java/com/android/internal/infra/TEST_MAPPING b/core/java/com/android/internal/infra/TEST_MAPPING
index e4550c0db135b4d4414d3e6abfbcbecd576bd1ba..35f0553d41d7f091e779566a0e3fe2a08f5e2fe3 100644
--- a/core/java/com/android/internal/infra/TEST_MAPPING
+++ b/core/java/com/android/internal/infra/TEST_MAPPING
@@ -9,15 +9,7 @@
]
},
{
- "name": "CtsPermissionTestCases",
- "options": [
- {
- "include-filter": "android.permission.cts.PermissionControllerTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
+ "name": "CtsPermissionTestCases_Platform"
},
{
"name": "FrameworksCoreTests_internal_infra"
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 53ef49bd3f65402c1031d9c9b5afdd8bfbbc8b16..d474c6db4f0286e198ba0845b61d6372f51f3d9b 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -127,7 +127,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
private Runnable mWaitForFinishTimedOut;
private static class JankInfo {
- long frameVsyncId;
+ final long frameVsyncId;
long totalDurationNanos;
boolean isFirstFrame;
boolean hwuiCallbackFired;
@@ -135,29 +135,42 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
@JankType int jankType;
@RefreshRate int refreshRate;
- static JankInfo createFromHwuiCallback(long frameVsyncId, long totalDurationNanos,
- boolean isFirstFrame) {
- return new JankInfo(frameVsyncId, true, false, JANK_NONE, UNKNOWN_REFRESH_RATE,
- totalDurationNanos, isFirstFrame);
+ static JankInfo createFromHwuiCallback(
+ long frameVsyncId, long totalDurationNanos, boolean isFirstFrame) {
+ return new JankInfo(frameVsyncId).update(totalDurationNanos, isFirstFrame);
}
- static JankInfo createFromSurfaceControlCallback(long frameVsyncId,
- @JankType int jankType, @RefreshRate int refreshRate) {
- return new JankInfo(
- frameVsyncId, false, true, jankType, refreshRate, 0, false /* isFirstFrame */);
+ static JankInfo createFromSurfaceControlCallback(SurfaceControl.JankData jankStat) {
+ return new JankInfo(jankStat.frameVsyncId).update(jankStat);
}
- private JankInfo(long frameVsyncId, boolean hwuiCallbackFired,
- boolean surfaceControlCallbackFired, @JankType int jankType,
- @RefreshRate int refreshRate,
- long totalDurationNanos, boolean isFirstFrame) {
+ private JankInfo(long frameVsyncId) {
this.frameVsyncId = frameVsyncId;
- this.hwuiCallbackFired = hwuiCallbackFired;
- this.surfaceControlCallbackFired = surfaceControlCallbackFired;
- this.jankType = jankType;
- this.refreshRate = refreshRate;
- this.totalDurationNanos = totalDurationNanos;
+ this.hwuiCallbackFired = false;
+ this.surfaceControlCallbackFired = false;
+ this.jankType = JANK_NONE;
+ this.refreshRate = UNKNOWN_REFRESH_RATE;
+ this.totalDurationNanos = 0;
+ this.isFirstFrame = false;
+ }
+
+ private JankInfo update(SurfaceControl.JankData jankStat) {
+ this.surfaceControlCallbackFired = true;
+ this.jankType = jankStat.jankType;
+ this.refreshRate = DisplayRefreshRate.getRefreshRate(jankStat.frameIntervalNs);
+ if (Flags.useSfFrameDuration()) {
+ this.totalDurationNanos = jankStat.actualAppFrameTimeNs;
+ }
+ return this;
+ }
+
+ private JankInfo update(long totalDurationNanos, boolean isFirstFrame) {
+ this.hwuiCallbackFired = true;
+ if (!Flags.useSfFrameDuration()) {
+ this.totalDurationNanos = totalDurationNanos;
+ }
this.isFirstFrame = isFirstFrame;
+ return this;
}
@Override
@@ -457,16 +470,12 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
if (!isInRange(jankStat.frameVsyncId)) {
continue;
}
- int refreshRate = DisplayRefreshRate.getRefreshRate(jankStat.frameIntervalNs);
JankInfo info = findJankInfo(jankStat.frameVsyncId);
if (info != null) {
- info.surfaceControlCallbackFired = true;
- info.jankType = jankStat.jankType;
- info.refreshRate = refreshRate;
+ info.update(jankStat);
} else {
mJankInfos.put((int) jankStat.frameVsyncId,
- JankInfo.createFromSurfaceControlCallback(
- jankStat.frameVsyncId, jankStat.jankType, refreshRate));
+ JankInfo.createFromSurfaceControlCallback(jankStat));
}
}
processJankInfos();
@@ -513,9 +522,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
}
JankInfo info = findJankInfo(frameVsyncId);
if (info != null) {
- info.hwuiCallbackFired = true;
- info.totalDurationNanos = totalDurationNanos;
- info.isFirstFrame = isFirstFrame;
+ info.update(totalDurationNanos, isFirstFrame);
} else {
mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback(
frameVsyncId, totalDurationNanos, isFirstFrame));
diff --git a/core/java/com/android/internal/jank/flags.aconfig b/core/java/com/android/internal/jank/flags.aconfig
new file mode 100644
index 0000000000000000000000000000000000000000..b6b8bc5ac44cbae58b4a277ed49c0c262915aef9
--- /dev/null
+++ b/core/java/com/android/internal/jank/flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.internal.jank"
+container: "system"
+
+flag {
+ name: "use_sf_frame_duration"
+ namespace: "android_platform_window_surfaces"
+ description: "Whether to get the frame duration from SurfaceFlinger, or HWUI"
+ bug: "354763298"
+ is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 07fa679a428a5a83ea6f2353a29d305a152ce033..dfb2884044f55524813d343401c89d4b7103523c 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -18,6 +18,10 @@ package com.android.internal.os;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
import com.android.internal.util.Preconditions;
@@ -55,18 +59,15 @@ import java.util.concurrent.atomic.AtomicReference;
*
* @hide
*/
-@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.LongArrayMultiStateCounter_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("LongArrayMultiStateCounter_host")
public final class LongArrayMultiStateCounter implements Parcelable {
/**
* Container for a native equivalent of a long[].
*/
- @android.ravenwood.annotation.RavenwoodKeepWholeClass
- @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution"
- + ".LongArrayMultiStateCounter_host$LongArrayContainer_host")
+ @RavenwoodKeepWholeClass
+ @RavenwoodRedirectionClass("LongArrayContainer_host")
public static class LongArrayContainer {
private static NativeAllocationRegistry sRegistry;
@@ -81,7 +82,7 @@ public final class LongArrayMultiStateCounter implements Parcelable {
registerNativeAllocation();
}
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodReplace
private void registerNativeAllocation() {
if (sRegistry == null) {
synchronized (LongArrayMultiStateCounter.class) {
@@ -140,18 +141,23 @@ public final class LongArrayMultiStateCounter implements Parcelable {
}
@CriticalNative
+ @RavenwoodRedirect
private static native long native_init(int length);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_getReleaseFunc();
@FastNative
+ @RavenwoodRedirect
private static native void native_setValues(long nativeObject, long[] array);
@FastNative
+ @RavenwoodRedirect
private static native void native_getValues(long nativeObject, long[] array);
@FastNative
+ @RavenwoodRedirect
private static native boolean native_combineValues(long nativeObject, long[] array,
int[] indexMap);
}
@@ -175,7 +181,7 @@ public final class LongArrayMultiStateCounter implements Parcelable {
registerNativeAllocation();
}
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodReplace
private void registerNativeAllocation() {
if (sRegistry == null) {
synchronized (LongArrayMultiStateCounter.class) {
@@ -374,57 +380,73 @@ public final class LongArrayMultiStateCounter implements Parcelable {
@CriticalNative
+ @RavenwoodRedirect
private static native long native_init(int stateCount, int arrayLength);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_getReleaseFunc();
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setEnabled(long nativeObject, boolean enabled,
long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setState(long nativeObject, int state, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_copyStatesFrom(long nativeObjectTarget,
long nativeObjectSource);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setValues(long nativeObject, int state,
long longArrayContainerNativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_updateValues(long nativeObject,
long longArrayContainerNativeObject, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_incrementValues(long nativeObject,
long longArrayContainerNativeObject, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_addCounts(long nativeObject,
long longArrayContainerNativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_reset(long nativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_getCounts(long nativeObject,
long longArrayContainerNativeObject, int state);
@FastNative
+ @RavenwoodRedirect
private static native String native_toString(long nativeObject);
@FastNative
+ @RavenwoodRedirect
private static native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
@FastNative
+ @RavenwoodRedirect
private static native long native_initFromParcel(Parcel parcel);
@CriticalNative
+ @RavenwoodRedirect
private static native int native_getStateCount(long nativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native int native_getArrayLength(long nativeObject);
}
diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java
index e5662c7d514517a08d7cced04af54c1e8098afc9..c386a86f590638c0c5a68d8f961e0816ef561d16 100644
--- a/core/java/com/android/internal/os/LongMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongMultiStateCounter.java
@@ -18,6 +18,10 @@ package com.android.internal.os;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
import com.android.internal.util.Preconditions;
@@ -55,9 +59,8 @@ import libcore.util.NativeAllocationRegistry;
*
* @hide
*/
-@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.LongMultiStateCounter_host")
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("LongMultiStateCounter_host")
public final class LongMultiStateCounter implements Parcelable {
private static NativeAllocationRegistry sRegistry;
@@ -82,7 +85,7 @@ public final class LongMultiStateCounter implements Parcelable {
mStateCount = native_getStateCount(mNativeObject);
}
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodReplace
private void registerNativeAllocation() {
if (sRegistry == null) {
synchronized (LongMultiStateCounter.class) {
@@ -210,43 +213,56 @@ public final class LongMultiStateCounter implements Parcelable {
@CriticalNative
+ @RavenwoodRedirect
private static native long native_init(int stateCount);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_getReleaseFunc();
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setEnabled(long nativeObject, boolean enabled,
long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_setState(long nativeObject, int state, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_updateValue(long nativeObject, long value, long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_incrementValue(long nativeObject, long increment,
long timestampMs);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_addCount(long nativeObject, long count);
@CriticalNative
+ @RavenwoodRedirect
private static native void native_reset(long nativeObject);
@CriticalNative
+ @RavenwoodRedirect
private static native long native_getCount(long nativeObject, int state);
@FastNative
+ @RavenwoodRedirect
private static native String native_toString(long nativeObject);
@FastNative
+ @RavenwoodRedirect
private static native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
@FastNative
+ @RavenwoodRedirect
private static native long native_initFromParcel(Parcel parcel);
@CriticalNative
+ @RavenwoodRedirect
private static native int native_getStateCount(long nativeObject);
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index cdac0979613758398fc5ad6c7bfd37ce3c5b6b70..1709ca78af4be6b1a7b86b2d347a0ba489ac7421 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -404,6 +404,17 @@ public class RuntimeInit {
}
public static void redirectLogStreams$ravenwood() {
+ if (sOut$ravenwood != null && sErr$ravenwood != null) {
+ return; // Already initialized.
+ }
+
+ // Make sure the Log class is loaded and the JNI methods are hooked up,
+ // before redirecting System.out/err.
+ // Otherwise, because ClassLoadHook tries to write to System.out, this would cause
+ // a circular initialization problem and would cause a UnsatisfiedLinkError
+ // on the JNI methods.
+ Log.isLoggable("X", Log.VERBOSE);
+
if (sOut$ravenwood == null) {
sOut$ravenwood = System.out;
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b9cc457fecc92de8cdd8127f8c7ae060d79908b2..2acda8ad71c1963127ae573213fb0a636615911e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -631,21 +631,20 @@ public class ZygoteInit {
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
- long capabilities = posixCapabilitiesAsBits(
- OsConstants.CAP_IPC_LOCK,
- OsConstants.CAP_KILL,
- OsConstants.CAP_NET_ADMIN,
- OsConstants.CAP_NET_BIND_SERVICE,
- OsConstants.CAP_NET_BROADCAST,
- OsConstants.CAP_NET_RAW,
- OsConstants.CAP_SYS_MODULE,
- OsConstants.CAP_SYS_NICE,
- OsConstants.CAP_SYS_PTRACE,
- OsConstants.CAP_SYS_TIME,
- OsConstants.CAP_SYS_TTY_CONFIG,
- OsConstants.CAP_WAKE_ALARM,
- OsConstants.CAP_BLOCK_SUSPEND
- );
+ long capabilities =
+ (1L << OsConstants.CAP_IPC_LOCK) |
+ (1L << OsConstants.CAP_KILL) |
+ (1L << OsConstants.CAP_NET_ADMIN) |
+ (1L << OsConstants.CAP_NET_BIND_SERVICE) |
+ (1L << OsConstants.CAP_NET_BROADCAST) |
+ (1L << OsConstants.CAP_NET_RAW) |
+ (1L << OsConstants.CAP_SYS_MODULE) |
+ (1L << OsConstants.CAP_SYS_NICE) |
+ (1L << OsConstants.CAP_SYS_PTRACE) |
+ (1L << OsConstants.CAP_SYS_TIME) |
+ (1L << OsConstants.CAP_SYS_TTY_CONFIG) |
+ (1L << OsConstants.CAP_WAKE_ALARM) |
+ (1L << OsConstants.CAP_BLOCK_SUSPEND);
/* Containers run without some capabilities, so drop any caps that are not available. */
StructCapUserHeader header = new StructCapUserHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
@@ -741,20 +740,6 @@ public class ZygoteInit {
return null;
}
- /**
- * Gets the bit array representation of the provided list of POSIX capabilities.
- */
- private static long posixCapabilitiesAsBits(int... capabilities) {
- long result = 0;
- for (int capability : capabilities) {
- if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
- throw new IllegalArgumentException(String.valueOf(capability));
- }
- result |= (1L << capability);
- }
- return result;
- }
-
/**
* This is the entry point for a Zygote process. It creates the Zygote server, loads resources,
* and handles other tasks related to preparing the process for forking into applications.
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 48283930595d3b39c107d655b1a20f97e2142b30..4708be8108c26bb89cd220bdfaaa63126aa0ed09 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -63,6 +63,9 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -114,6 +117,7 @@ import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import com.android.window.flags.Flags;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/** @hide */
@@ -1140,7 +1144,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
mDrawLegacyNavigationBarBackground =
((requestedVisibleTypes | mLastForceConsumingTypes)
& WindowInsets.Type.navigationBars()) != 0
- && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
+ && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+ && navBarSize > 0;
if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
mDrawLegacyNavigationBarBackgroundHandled =
mWindow.onDrawLegacyNavigationBarBackgroundChanged(
@@ -1348,8 +1353,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
mCrossWindowBlurEnabled = enabled;
updateBackgroundBlurRadius();
};
+ // The executor to receive callback {@link mCrossWindowBlurEnabledListener}. It
+ // should be the executor for this {@link DecorView}'s ui thread (not necessarily
+ // the main thread).
+ final Executor executor = Looper.myLooper() == Looper.getMainLooper()
+ ? getContext().getMainExecutor()
+ : new HandlerExecutor(new Handler(Looper.myLooper()));
getContext().getSystemService(WindowManager.class)
- .addCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);
+ .addCrossWindowBlurEnabledListener(
+ executor, mCrossWindowBlurEnabledListener);
getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
} else {
updateBackgroundBlurRadius();
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e14249cc028bf4810454802724504c2e8dc91e6b..e0529b339d4afd462388befdf662989092e88cbe 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3333,6 +3333,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
Bundle args = new Bundle();
args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, event.getDeviceId());
args.putLong(Intent.EXTRA_TIME, event.getEventTime());
+ args.putBoolean(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, true);
((SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE))
.launchAssist(args);
return true;
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index b2fdf17f8564b7962deff5e7de50ebe2dade59fc..e440dc9053fd4fede238f6ce139d1cb10e5c58e8 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -34,7 +34,6 @@ import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Gro
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.INTERNED_DATA;
import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_MESSAGE;
@@ -72,7 +71,6 @@ import com.android.internal.protolog.common.LogLevel;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -99,13 +97,11 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
public static final String NULL_STRING = "null";
private final AtomicInteger mTracingInstances = new AtomicInteger();
- private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
- this::onTracingInstanceStart,
- this::onTracingFlush,
- this::onTracingInstanceStop
- );
+ @NonNull
+ private final ProtoLogDataSource mDataSource;
@Nullable
private final ProtoLogViewerConfigReader mViewerConfigReader;
+ @Deprecated
@Nullable
private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
@NonNull
@@ -151,13 +147,29 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
cacheUpdater, groups);
}
+ @Deprecated
@VisibleForTesting
public PerfettoProtoLogImpl(
@Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
@Nullable ProtoLogViewerConfigReader viewerConfigReader,
@NonNull Runnable cacheUpdater,
- @NonNull IProtoLogGroup[] groups) {
- this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater, groups);
+ @NonNull IProtoLogGroup[] groups,
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @NonNull ProtoLogConfigurationService configurationService) {
+ this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater,
+ groups, dataSourceBuilder, configurationService);
+ }
+
+ @VisibleForTesting
+ public PerfettoProtoLogImpl(
+ @Nullable String viewerConfigFilePath,
+ @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+ @NonNull Runnable cacheUpdater,
+ @NonNull IProtoLogGroup[] groups,
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @NonNull ProtoLogConfigurationService configurationService) {
+ this(viewerConfigFilePath, null, viewerConfigReader, cacheUpdater,
+ groups, dataSourceBuilder, configurationService);
}
private PerfettoProtoLogImpl(
@@ -166,11 +178,31 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
@Nullable ProtoLogViewerConfigReader viewerConfigReader,
@NonNull Runnable cacheUpdater,
@NonNull IProtoLogGroup[] groups) {
+ this(viewerConfigFilePath, viewerConfigInputStreamProvider, viewerConfigReader,
+ cacheUpdater, groups,
+ ProtoLogDataSource::new,
+ IProtoLogConfigurationService.Stub
+ .asInterface(ServiceManager.getService(PROTOLOG_CONFIGURATION_SERVICE))
+ );
+ }
+
+ private PerfettoProtoLogImpl(
+ @Nullable String viewerConfigFilePath,
+ @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
+ @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+ @NonNull Runnable cacheUpdater,
+ @NonNull IProtoLogGroup[] groups,
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @Nullable IProtoLogConfigurationService configurationService) {
if (viewerConfigFilePath != null && viewerConfigInputStreamProvider != null) {
throw new RuntimeException("Only one of viewerConfigFilePath and "
+ "viewerConfigInputStreamProvider can be set");
}
+ mDataSource = dataSourceBuilder.build(
+ this::onTracingInstanceStart,
+ this::onTracingFlush,
+ this::onTracingInstanceStop);
Producer.init(InitArguments.DEFAULTS);
DataSourceParams params =
new DataSourceParams.Builder()
@@ -186,11 +218,9 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
registerGroupsLocally(groups);
if (android.tracing.Flags.clientSideProtoLogging()) {
- mProtoLogConfigurationService =
- IProtoLogConfigurationService.Stub.asInterface(ServiceManager.getService(
- PROTOLOG_CONFIGURATION_SERVICE));
+ mProtoLogConfigurationService = configurationService;
Objects.requireNonNull(mProtoLogConfigurationService,
- "ServiceManager returned a null ProtoLog service");
+ "ServiceManager returned a null ProtoLog Configuration Service");
try {
var args = new ProtoLogConfigurationService.RegisterClientArgs();
@@ -431,6 +461,7 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
Log.d(LOG_TAG, "Finished onTracingFlush");
}
+ @Deprecated
private void dumpViewerConfig() {
if (mViewerConfigInputStreamProvider == null) {
// No viewer config available
@@ -439,103 +470,29 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
Log.d(LOG_TAG, "Dumping viewer config to trace");
- mDataSource.trace(ctx -> {
- try {
- ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
-
- final ProtoOutputStream os = ctx.newTracePacket();
-
- os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
-
- final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- if (pis.getFieldNumber() == (int) MESSAGES) {
- writeViewerConfigMessage(pis, os);
- }
-
- if (pis.getFieldNumber() == (int) GROUPS) {
- writeViewerConfigGroup(pis, os);
- }
- }
-
- os.end(outProtologViewerConfigToken);
- } catch (IOException e) {
- Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
- }
- });
+ Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider);
Log.d(LOG_TAG, "Dumped viewer config to trace");
}
- private static void writeViewerConfigGroup(
- ProtoInputStream pis, ProtoOutputStream os) throws IOException {
- final long inGroupToken = pis.start(GROUPS);
- final long outGroupToken = os.start(GROUPS);
-
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) ID:
- int id = pis.readInt(ID);
- os.write(ID, id);
- break;
- case (int) NAME:
- String name = pis.readString(NAME);
- os.write(NAME, name);
- break;
- case (int) TAG:
- String tag = pis.readString(TAG);
- os.write(TAG, tag);
- break;
- default:
- throw new RuntimeException(
- "Unexpected field id " + pis.getFieldNumber());
- }
- }
-
- pis.end(inGroupToken);
- os.end(outGroupToken);
- }
-
- private static void writeViewerConfigMessage(
- ProtoInputStream pis, ProtoOutputStream os) throws IOException {
- final long inMessageToken = pis.start(MESSAGES);
- final long outMessagesToken = os.start(MESSAGES);
-
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) MessageData.MESSAGE_ID:
- os.write(MessageData.MESSAGE_ID,
- pis.readLong(MessageData.MESSAGE_ID));
- break;
- case (int) MESSAGE:
- os.write(MESSAGE, pis.readString(MESSAGE));
- break;
- case (int) LEVEL:
- os.write(LEVEL, pis.readInt(LEVEL));
- break;
- case (int) GROUP_ID:
- os.write(GROUP_ID, pis.readInt(GROUP_ID));
- break;
- case (int) LOCATION:
- os.write(LOCATION, pis.readString(LOCATION));
- break;
- default:
- throw new RuntimeException(
- "Unexpected field id " + pis.getFieldNumber());
- }
- }
-
- pis.end(inMessageToken);
- os.end(outMessagesToken);
- }
-
private void logToLogcat(String tag, LogLevel level, Message message,
@Nullable Object[] args) {
String messageString;
if (mViewerConfigReader == null) {
messageString = message.getMessage();
+
+ if (messageString == null) {
+ Log.e(LOG_TAG, "Failed to decode message for logcat. "
+ + "Message not available without ViewerConfig to decode the hash.");
+ }
} else {
messageString = message.getMessage(mViewerConfigReader);
+
+ if (messageString == null) {
+ Log.e(LOG_TAG, "Failed to decode message for logcat. "
+ + "Message hash either not available in viewerConfig file or "
+ + "not loaded into memory from file before decoding.");
+ }
}
if (messageString == null) {
@@ -733,12 +690,16 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
incrementalState.protologMessageInterningSet.add(messageHash);
final ProtoOutputStream os = ctx.newTracePacket();
+
+ // Dependent on the ProtoLog viewer config packet that contains the group information.
+ os.write(SEQUENCE_FLAGS, SEQ_NEEDS_INCREMENTAL_STATE);
+
final long protologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
final long messageConfigToken = os.start(MESSAGES);
os.write(MessageData.MESSAGE_ID, messageHash);
os.write(MESSAGE, message);
- os.write(LEVEL, level.ordinal());
+ os.write(LEVEL, level.id);
os.write(GROUP_ID, logGroup.getId());
os.end(messageConfigToken);
@@ -930,17 +891,19 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
}
private static class Message {
+ @Nullable
private final Long mMessageHash;
- private final Integer mMessageMask;
+ private final int mMessageMask;
+ @Nullable
private final String mMessageString;
- private Message(Long messageHash, int messageMask) {
+ private Message(long messageHash, int messageMask) {
this.mMessageHash = messageHash;
this.mMessageMask = messageMask;
this.mMessageString = null;
}
- private Message(String messageString) {
+ private Message(@NonNull String messageString) {
this.mMessageHash = null;
final List argTypes = LogDataType.parseFormatString(messageString);
this.mMessageMask = LogDataType.logDataTypesToBitMask(argTypes);
@@ -951,16 +914,22 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
return mMessageMask;
}
+ @Nullable
private String getMessage() {
return mMessageString;
}
+ @Nullable
private String getMessage(@NonNull ProtoLogViewerConfigReader viewerConfigReader) {
if (mMessageString != null) {
return mMessageString;
}
- return viewerConfigReader.getViewerString(mMessageHash);
+ if (mMessageHash != null) {
+ return viewerConfigReader.getViewerString(mMessageHash);
+ }
+
+ throw new RuntimeException("Both mMessageString and mMessageHash should never be null");
}
}
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
index 17657387067966669362f067412cbccd3f470cd6..7031d694f09c48e22f8bd5168a82dd61536e2321 100644
--- a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
+++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
@@ -23,10 +23,9 @@ import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Gro
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
-import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_VIEWER_CONFIG;
-import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,7 +34,6 @@ import android.content.Context;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.os.SystemClock;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;
@@ -74,11 +72,7 @@ import java.util.TreeMap;
public final class ProtoLogConfigurationService extends IProtoLogConfigurationService.Stub {
private static final String LOG_TAG = "ProtoLogConfigurationService";
- private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
- this::onTracingInstanceStart,
- this::onTracingInstanceFlush,
- this::onTracingInstanceStop
- );
+ private final ProtoLogDataSource mDataSource;
/**
* Keeps track of how many of each viewer config file is currently registered.
@@ -115,11 +109,29 @@ public final class ProtoLogConfigurationService extends IProtoLogConfigurationSe
private final ViewerConfigFileTracer mViewerConfigFileTracer;
public ProtoLogConfigurationService() {
- this(ProtoLogConfigurationService::dumpTransitionTraceConfig);
+ this(ProtoLogDataSource::new, ProtoLogConfigurationService::dumpTransitionTraceConfig);
+ }
+
+ @VisibleForTesting
+ public ProtoLogConfigurationService(@NonNull ProtoLogDataSourceBuilder dataSourceBuilder) {
+ this(dataSourceBuilder, ProtoLogConfigurationService::dumpTransitionTraceConfig);
}
@VisibleForTesting
public ProtoLogConfigurationService(@NonNull ViewerConfigFileTracer tracer) {
+ this(ProtoLogDataSource::new, tracer);
+ }
+
+ @VisibleForTesting
+ public ProtoLogConfigurationService(
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @NonNull ViewerConfigFileTracer tracer) {
+ mDataSource = dataSourceBuilder.build(
+ this::onTracingInstanceStart,
+ this::onTracingInstanceFlush,
+ this::onTracingInstanceStop
+ );
+
// Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be
// receive the lifecycle callbacks of the datasource and write the viewer configs if and
// when required to the datasource.
@@ -210,8 +222,7 @@ public final class ProtoLogConfigurationService extends IProtoLogConfigurationSe
* want to write to the trace buffer.
* @throws FileNotFoundException if the viewerConfigFilePath is invalid.
*/
- void trace(@NonNull ProtoLogDataSource dataSource, @NonNull String viewerConfigFilePath)
- throws FileNotFoundException;
+ void trace(@NonNull ProtoLogDataSource dataSource, @NonNull String viewerConfigFilePath);
}
@Override
@@ -351,11 +362,7 @@ public final class ProtoLogConfigurationService extends IProtoLogConfigurationSe
private void onTracingInstanceFlush() {
for (String fileName : mConfigFileCounts.keySet()) {
- try {
- mViewerConfigFileTracer.trace(mDataSource, fileName);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
+ mViewerConfigFileTracer.trace(mDataSource, fileName);
}
}
@@ -364,26 +371,13 @@ public final class ProtoLogConfigurationService extends IProtoLogConfigurationSe
}
private static void dumpTransitionTraceConfig(@NonNull ProtoLogDataSource dataSource,
- @NonNull String viewerConfigFilePath) throws FileNotFoundException {
- final var pis = new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
-
- dataSource.trace(ctx -> {
+ @NonNull String viewerConfigFilePath) {
+ Utils.dumpViewerConfig(dataSource, () -> {
try {
- final ProtoOutputStream os = ctx.newTracePacket();
-
- os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
-
- final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) MESSAGES -> writeViewerConfigMessage(pis, os);
- case (int) GROUPS -> writeViewerConfigGroup(pis, os);
- }
- }
-
- os.end(outProtologViewerConfigToken);
- } catch (IOException e) {
- Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
+ return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(
+ "Failed to load viewer config file " + viewerConfigFilePath, e);
}
});
}
@@ -396,11 +390,7 @@ public final class ProtoLogConfigurationService extends IProtoLogConfigurationSe
mConfigFileCounts.put(configFile, newCount);
boolean lastProcessWithViewerConfig = newCount == 0;
if (lastProcessWithViewerConfig) {
- try {
- mViewerConfigFileTracer.trace(mDataSource, configFile);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
+ mViewerConfigFileTracer.trace(mDataSource, configFile);
}
}
}
@@ -446,6 +436,7 @@ public final class ProtoLogConfigurationService extends IProtoLogConfigurationSe
case (int) MESSAGE -> os.write(MESSAGE, pis.readString(MESSAGE));
case (int) LEVEL -> os.write(LEVEL, pis.readInt(LEVEL));
case (int) GROUP_ID -> os.write(GROUP_ID, pis.readInt(GROUP_ID));
+ case (int) LOCATION -> os.write(LOCATION, pis.readString(LOCATION));
default ->
throw new RuntimeException(
"Unexpected field id " + pis.getFieldNumber());
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
index 5c06b87f78acc83ca93fc3ddb433cab45d3cc4b9..0afb135ac6d98ecf8c8bb6c9f7e6828b28096fff 100644
--- a/core/java/com/android/internal/protolog/ProtoLogDataSource.java
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
@@ -25,6 +25,7 @@ import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.COLL
import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.GROUP_NAME;
import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.LOG_FROM;
+import android.annotation.NonNull;
import android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
import android.internal.perfetto.protos.ProtologCommon;
import android.tracing.perfetto.CreateIncrementalStateArgs;
@@ -37,6 +38,7 @@ import android.tracing.perfetto.StopCallbackArguments;
import android.util.proto.ProtoInputStream;
import android.util.proto.WireTypeMismatchException;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.LogLevel;
import java.io.IOException;
@@ -48,21 +50,37 @@ import java.util.Set;
public class ProtoLogDataSource extends DataSource {
+ private static final String DATASOURCE_NAME = "android.protolog";
+ @NonNull
private final Instance.TracingInstanceStartCallback mOnStart;
+ @NonNull
private final Runnable mOnFlush;
+ @NonNull
private final Instance.TracingInstanceStopCallback mOnStop;
- public ProtoLogDataSource(Instance.TracingInstanceStartCallback onStart, Runnable onFlush,
- Instance.TracingInstanceStopCallback onStop) {
- super("android.protolog");
+ public ProtoLogDataSource(
+ @NonNull Instance.TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull Instance.TracingInstanceStopCallback onStop) {
+ this(onStart, onFlush, onStop, DATASOURCE_NAME);
+ }
+
+ @VisibleForTesting
+ public ProtoLogDataSource(
+ @NonNull Instance.TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull Instance.TracingInstanceStopCallback onStop,
+ @NonNull String dataSourceName) {
+ super(dataSourceName);
this.mOnStart = onStart;
this.mOnFlush = onFlush;
this.mOnStop = onStop;
}
@Override
- public Instance createInstance(ProtoInputStream configStream, int instanceIndex) {
+ @NonNull
+ public Instance createInstance(@NonNull ProtoInputStream configStream, int instanceIndex) {
ProtoLogConfig config = null;
try {
@@ -92,7 +110,8 @@ public class ProtoLogDataSource extends DataSource args) {
+ @NonNull
+ public TlsState createTlsState(@NonNull CreateTlsStateArgs args) {
try (Instance dsInstance = args.getDataSourceInstanceLocked()) {
if (dsInstance == null) {
// Datasource instance has been removed
@@ -103,14 +122,17 @@ public class ProtoLogDataSource extends DataSource args) {
+ @NonNull
+ public IncrementalState createIncrementalState(
+ @NonNull CreateIncrementalStateArgs args) {
return new IncrementalState();
}
public static class TlsState {
+ @NonNull
private final ProtoLogConfig mConfig;
- private TlsState(ProtoLogConfig config) {
+ private TlsState(@NonNull ProtoLogConfig config) {
this.mConfig = config;
}
@@ -195,7 +217,7 @@ public class ProtoLogDataSource extends DataSource dataSource,
+ @NonNull DataSource dataSource,
int instanceIdx,
- ProtoLogConfig config,
- TracingInstanceStartCallback onStart,
- Runnable onFlush,
- TracingInstanceStopCallback onStop
+ @NonNull ProtoLogConfig config,
+ @NonNull TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull TracingInstanceStopCallback onStop
) {
super(dataSource, instanceIdx);
this.mInstanceIndex = instanceIdx;
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java b/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..da78b621bf9016501a32d7daaed48beca6e0f145
--- /dev/null
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import android.annotation.NonNull;
+
+public interface ProtoLogDataSourceBuilder {
+ /**
+ * Builder method for the DataSource the PerfettoProtoLogImpl is going to us.
+ * @param onStart The onStart callback that should be used by the created datasource.
+ * @param onFlush The onFlush callback that should be used by the created datasource.
+ * @param onStop The onStop callback that should be used by the created datasource.
+ * @return A new DataSource that uses the provided callbacks.
+ */
+ @NonNull
+ ProtoLogDataSource build(
+ @NonNull ProtoLogDataSource.Instance.TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull ProtoLogDataSource.Instance.TracingInstanceStopCallback onStop);
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index da6d8cff689033ef61f74a7b77065dff1050e5e5..7bdcf2d14b19c82168b93e830679c898f5473c6c 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -23,6 +23,7 @@ import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.LO
import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.VIEWER_CONFIG_PATH;
import android.annotation.Nullable;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.IProtoLog;
@@ -37,6 +38,8 @@ import java.util.TreeMap;
* A service for the ProtoLog logging system.
*/
public class ProtoLogImpl {
+ private static final String LOG_TAG = "ProtoLogImpl";
+
private static IProtoLog sServiceInstance = null;
@ProtoLogToolInjected(VIEWER_CONFIG_PATH)
@@ -97,6 +100,9 @@ public class ProtoLogImpl {
*/
public static synchronized IProtoLog getSingleInstance() {
if (sServiceInstance == null) {
+ Log.i(LOG_TAG, "Setting up " + ProtoLogImpl.class.getSimpleName() + " with "
+ + "viewerConfigPath = " + sViewerConfigPath);
+
final var groups = sLogGroups.values().toArray(new IProtoLogGroup[0]);
if (android.tracing.Flags.perfettoProtologTracing()) {
@@ -105,6 +111,9 @@ public class ProtoLogImpl {
// TODO(b/353530422): Remove - temporary fix to unblock b/352290057
// In some tests the viewer config file might not exist in which we don't
// want to provide config path to the user
+ Log.w(LOG_TAG, "Failed to find viewerConfigFile when setting up "
+ + ProtoLogImpl.class.getSimpleName() + ". "
+ + "Setting up without a viewer config instead...");
sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater, groups);
} else {
sServiceInstance =
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 38ca0d8f75e87cd8b61467549b0e3afe94feb8aa..0a80e006d5bca7988f436c22a52e670879e75b27 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -3,7 +3,6 @@ package com.android.internal.protolog;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
-
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
@@ -11,7 +10,6 @@ import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Mes
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.util.Log;
import android.util.LongSparseArray;
import android.util.proto.ProtoInputStream;
@@ -26,7 +24,9 @@ import java.util.TreeMap;
public class ProtoLogViewerConfigReader {
@NonNull
private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
+ @NonNull
private final Map> mGroupHashes = new TreeMap<>();
+ @NonNull
private final LongSparseArray mLogMessageMap = new LongSparseArray<>();
public ProtoLogViewerConfigReader(
@@ -38,18 +38,26 @@ public class ProtoLogViewerConfigReader {
* Returns message format string for its hash or null if unavailable
* or the viewer config is not loaded into memory.
*/
+ @Nullable
public synchronized String getViewerString(long messageHash) {
return mLogMessageMap.get(messageHash);
}
- public synchronized void loadViewerConfig(String[] groups) {
+ /**
+ * Load the viewer configs for the target groups into memory.
+ * Only viewer configs loaded into memory can be required. So this must be called for all groups
+ * we want to query before we query their viewer config.
+ *
+ * @param groups Groups to load the viewer configs from file into memory.
+ */
+ public synchronized void loadViewerConfig(@NonNull String[] groups) {
loadViewerConfig(groups, (message) -> {});
}
/**
* Loads the viewer config into memory. No-op if already loaded in memory.
*/
- public synchronized void loadViewerConfig(String[] groups, @NonNull ILogger logger) {
+ public synchronized void loadViewerConfig(@NonNull String[] groups, @NonNull ILogger logger) {
for (String group : groups) {
if (mGroupHashes.containsKey(group)) {
continue;
@@ -70,14 +78,14 @@ public class ProtoLogViewerConfigReader {
}
}
- public synchronized void unloadViewerConfig(String[] groups) {
+ public synchronized void unloadViewerConfig(@NonNull String[] groups) {
unloadViewerConfig(groups, (message) -> {});
}
/**
* Unload the viewer config from memory.
*/
- public synchronized void unloadViewerConfig(String[] groups, @NonNull ILogger logger) {
+ public synchronized void unloadViewerConfig(@NonNull String[] groups, @NonNull ILogger logger) {
for (String group : groups) {
if (!mGroupHashes.containsKey(group)) {
continue;
@@ -91,8 +99,10 @@ public class ProtoLogViewerConfigReader {
}
}
- private Map loadViewerConfigMappingForGroup(String group) throws IOException {
- Long targetGroupId = loadGroupId(group);
+ @NonNull
+ private Map loadViewerConfigMappingForGroup(@NonNull String group)
+ throws IOException {
+ long targetGroupId = loadGroupId(group);
final Map hashesForGroup = new TreeMap<>();
final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
@@ -141,7 +151,7 @@ public class ProtoLogViewerConfigReader {
return hashesForGroup;
}
- private Long loadGroupId(String group) throws IOException {
+ private long loadGroupId(@NonNull String group) throws IOException {
final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
diff --git a/core/java/com/android/internal/protolog/TEST_MAPPING b/core/java/com/android/internal/protolog/TEST_MAPPING
new file mode 100644
index 0000000000000000000000000000000000000000..37d57eed8cf4bf46eec38eeea2d69547e3903f71
--- /dev/null
+++ b/core/java/com/android/internal/protolog/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "ProtologPerfTests"
+ }
+ ]
+}
diff --git a/core/java/com/android/internal/protolog/Utils.java b/core/java/com/android/internal/protolog/Utils.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e6ba309c04615fd210a8d1c30b1d18627a0f409
--- /dev/null
+++ b/core/java/com/android/internal/protolog/Utils.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.TAG;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_VIEWER_CONFIG;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
+
+import android.annotation.NonNull;
+import android.internal.perfetto.protos.Protolog;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+
+public class Utils {
+ private static final String LOG_TAG = "ProtoLogUtils";
+
+ /**
+ * Dump the viewer config provided by the input stream to the target datasource.
+ * @param dataSource The datasource to dump the ProtoLog viewer config to.
+ * @param viewerConfigInputStreamProvider The InputStream that provided the proto viewer config.
+ */
+ public static void dumpViewerConfig(@NonNull ProtoLogDataSource dataSource,
+ @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) {
+ dataSource.trace(ctx -> {
+ try {
+ ProtoInputStream pis = viewerConfigInputStreamProvider.getInputStream();
+
+ final ProtoOutputStream os = ctx.newTracePacket();
+
+ os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
+
+ final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) MESSAGES) {
+ writeViewerConfigMessage(pis, os);
+ }
+
+ if (pis.getFieldNumber() == (int) GROUPS) {
+ writeViewerConfigGroup(pis, os);
+ }
+ }
+
+ os.end(outProtologViewerConfigToken);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump to datasource", e);
+ }
+ });
+ }
+
+ private static void writeViewerConfigGroup(
+ @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
+ final long inGroupToken = pis.start(GROUPS);
+ final long outGroupToken = os.start(GROUPS);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) ID:
+ int id = pis.readInt(ID);
+ os.write(ID, id);
+ break;
+ case (int) NAME:
+ String name = pis.readString(NAME);
+ os.write(NAME, name);
+ break;
+ case (int) TAG:
+ String tag = pis.readString(TAG);
+ os.write(TAG, tag);
+ break;
+ default:
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inGroupToken);
+ os.end(outGroupToken);
+ }
+
+ private static void writeViewerConfigMessage(
+ ProtoInputStream pis, ProtoOutputStream os) throws IOException {
+ final long inMessageToken = pis.start(MESSAGES);
+ final long outMessagesToken = os.start(MESSAGES);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID:
+ os.write(Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID,
+ pis.readLong(Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID));
+ break;
+ case (int) MESSAGE:
+ os.write(MESSAGE, pis.readString(MESSAGE));
+ break;
+ case (int) LEVEL:
+ os.write(LEVEL, pis.readInt(LEVEL));
+ break;
+ case (int) GROUP_ID:
+ os.write(GROUP_ID, pis.readInt(GROUP_ID));
+ break;
+ case (int) LOCATION:
+ os.write(LOCATION, pis.readString(LOCATION));
+ break;
+ default:
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inMessageToken);
+ os.end(outMessagesToken);
+ }
+
+}
diff --git a/core/java/com/android/internal/protolog/common/LogLevel.java b/core/java/com/android/internal/protolog/common/LogLevel.java
index 16c34e1f333e80115dd69439e13f532173e11a39..b5541ae81c2d2206b2136a64830fc0e2c2078ac4 100644
--- a/core/java/com/android/internal/protolog/common/LogLevel.java
+++ b/core/java/com/android/internal/protolog/common/LogLevel.java
@@ -17,10 +17,18 @@
package com.android.internal.protolog.common;
public enum LogLevel {
- DEBUG("d"), VERBOSE("v"), INFO("i"), WARN("w"), ERROR("e"), WTF("wtf");
+ DEBUG("d", 1),
+ VERBOSE("v", 2),
+ INFO("i", 3),
+ WARN("w", 4),
+ ERROR("e", 5),
+ WTF("wtf", 6);
public final String shortCode;
- LogLevel(String shortCode) {
+ public final int id;
+
+ LogLevel(String shortCode, int id) {
this.shortCode = shortCode;
+ this.id = id;
}
}
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index ee3a3c27ca7718d1b8e6d1755bc206b89142ff4f..30b160ab161bc236f4cca7df7bf7cf20adc4dec3 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -16,15 +16,15 @@
package com.android.internal.ravenwood;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.ravenwood.annotation.RavenwoodReplace;
/**
* Class to interact with the Ravenwood environment.
*/
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass(
- "com.android.platform.test.ravenwood.nativesubstitution.RavenwoodEnvironment_host")
+@RavenwoodRedirectionClass("RavenwoodEnvironment_host")
public final class RavenwoodEnvironment {
public static final String TAG = "RavenwoodEnvironment";
@@ -40,7 +40,7 @@ public final class RavenwoodEnvironment {
ensureRavenwoodInitialized();
}
- private static RuntimeException notSupportedOnDevice() {
+ public static RuntimeException notSupportedOnDevice() {
return new UnsupportedOperationException("This method can only be used on Ravenwood");
}
@@ -56,12 +56,10 @@ public final class RavenwoodEnvironment {
*
* No-op if called on the device side.
*/
- @RavenwoodReplace
+ @RavenwoodRedirect
public static void ensureRavenwoodInitialized() {
}
- private static native void ensureRavenwoodInitialized$ravenwood();
-
/**
* USE IT SPARINGLY! Returns true if it's running on Ravenwood, hostside test environment.
*
@@ -87,13 +85,11 @@ public final class RavenwoodEnvironment {
* Get the object back from the address obtained from
* {@link dalvik.system.VMRuntime#addressOf(Object)}.
*/
- @RavenwoodReplace
+ @RavenwoodRedirect
public T fromAddress(long address) {
throw notSupportedOnDevice();
}
- private native T fromAddress$ravenwood(long address);
-
/**
* See {@link Workaround}. It's only usable on Ravenwood.
*/
@@ -109,13 +105,11 @@ public final class RavenwoodEnvironment {
/**
* @return the "ravenwood-runtime" directory.
*/
- @RavenwoodReplace
+ @RavenwoodRedirect
public String getRavenwoodRuntimePath() {
throw notSupportedOnDevice();
}
- private native String getRavenwoodRuntimePath$ravenwood();
-
/**
* A set of APIs used to work around missing features on Ravenwood. Ideally, this class should
* be empty, and all its APIs should be able to be implemented properly.
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java
index f8a143693c83b38ae15e5ef9e1163ca507df316e..1938cdb0ba847f8fd5c5aeec8c3d4ba516c3a329 100644
--- a/core/java/com/android/internal/statusbar/StatusBarIcon.java
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java
@@ -51,6 +51,19 @@ public class StatusBarIcon implements Parcelable {
ResourceIcon
}
+ public enum Shape {
+ /**
+ * Icon view should use WRAP_CONTENT -- so that the horizontal space occupied depends on the
+ * icon's shape (skinny/fat icons take less/more). Most icons will want to use this option
+ * for a nicer-looking overall spacing in the status bar, as long as the icon is "known"
+ * (i.e. not coming from a 3P package).
+ */
+ WRAP_CONTENT,
+
+ /** Icon should always be displayed in a space as wide as the status bar is tall. */
+ FIXED_SPACE,
+ }
+
public UserHandle user;
public String pkg;
public Icon icon;
@@ -59,6 +72,7 @@ public class StatusBarIcon implements Parcelable {
public int number;
public CharSequence contentDescription;
public Type type;
+ public Shape shape;
/**
* Optional {@link Drawable} corresponding to {@link #icon}. This field is not parcelable, so
@@ -68,7 +82,7 @@ public class StatusBarIcon implements Parcelable {
@Nullable public Drawable preloadedIcon;
public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
- CharSequence contentDescription, Type type) {
+ CharSequence contentDescription, Type type, Shape shape) {
if (icon.getType() == Icon.TYPE_RESOURCE
&& TextUtils.isEmpty(icon.getResPackage())) {
// This is an odd situation where someone's managed to hand us an icon without a
@@ -83,6 +97,13 @@ public class StatusBarIcon implements Parcelable {
this.number = number;
this.contentDescription = contentDescription;
this.type = type;
+ this.shape = shape;
+ }
+
+ public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
+ CharSequence contentDescription, Type type) {
+ this(user, resPackage, icon, iconLevel, number, contentDescription, type,
+ Shape.WRAP_CONTENT);
}
public StatusBarIcon(String iconPackage, UserHandle user,
@@ -107,7 +128,7 @@ public class StatusBarIcon implements Parcelable {
@Override
public StatusBarIcon clone() {
StatusBarIcon that = new StatusBarIcon(this.user, this.pkg, this.icon,
- this.iconLevel, this.number, this.contentDescription, this.type);
+ this.iconLevel, this.number, this.contentDescription, this.type, this.shape);
that.visible = this.visible;
that.preloadedIcon = this.preloadedIcon;
return that;
@@ -129,6 +150,7 @@ public class StatusBarIcon implements Parcelable {
this.number = in.readInt();
this.contentDescription = in.readCharSequence();
this.type = Type.valueOf(in.readString());
+ this.shape = Shape.valueOf(in.readString());
}
public void writeToParcel(Parcel out, int flags) {
@@ -140,6 +162,7 @@ public class StatusBarIcon implements Parcelable {
out.writeInt(this.number);
out.writeCharSequence(this.contentDescription);
out.writeString(this.type.name());
+ out.writeString(this.shape.name());
}
public int describeContents() {
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b51678e82ed075e1d8a8c6d8644d4fa1c2db01f4..efbf8871445375804b528dc120507c0efd327b36 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -76,10 +76,10 @@ interface IInputMethodManager {
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in ImeTracker.Token statsToken, int flags, int lastClickToolType,
- in @nullable ResultReceiver resultReceiver, int reason);
+ in @nullable ResultReceiver resultReceiver, int reason, boolean async);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in ImeTracker.Token statsToken, int flags,
- in @nullable ResultReceiver resultReceiver, int reason);
+ in @nullable ResultReceiver resultReceiver, int reason, boolean async);
/**
* A test API for CTS to request hiding the current soft input window, with the request origin
@@ -120,7 +120,8 @@ interface IInputMethodManager {
in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, int userId,
- in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+ in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 11c220b14bcc924d77c322c82a3341f1c2dd42a3..0ec55f958f38af6cdb88ef24a7a0343c93238d6d 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -120,6 +120,7 @@ public class LockPatternView extends View {
private static final String TAG = "LockPatternView";
private OnPatternListener mOnPatternListener;
+ private ExternalHapticsPlayer mExternalHapticsPlayer;
@UnsupportedAppUsage
private final ArrayList mPattern = new ArrayList(9);
@@ -317,6 +318,13 @@ public class LockPatternView extends View {
void onPatternDetected(List| pattern);
}
+ /** An external haptics player for pattern updates. */
+ public interface ExternalHapticsPlayer{
+
+ /** Perform haptic feedback when a cell is added to the pattern. */
+ void performCellAddedFeedback();
+ }
+
public LockPatternView(Context context) {
this(context, null);
}
@@ -460,6 +468,15 @@ public class LockPatternView extends View {
mOnPatternListener = onPatternListener;
}
+ /**
+ * Set the external haptics player for feedback on pattern detection.
+ * @param player The external player.
+ */
+ @UnsupportedAppUsage
+ public void setExternalHapticsPlayer(ExternalHapticsPlayer player) {
+ mExternalHapticsPlayer = player;
+ }
+
/**
* Set the pattern explicitely (rather than waiting for the user to input
* a pattern).
@@ -847,6 +864,16 @@ public class LockPatternView extends View {
return null;
}
+ @Override
+ public boolean performHapticFeedback(int feedbackConstant, int flags) {
+ if (mExternalHapticsPlayer != null) {
+ mExternalHapticsPlayer.performCellAddedFeedback();
+ return true;
+ } else {
+ return super.performHapticFeedback(feedbackConstant, flags);
+ }
+ }
+
private void addCellToPattern(Cell newCell) {
mPatternDrawLookup[newCell.getRow()][newCell.getColumn()] = true;
mPattern.add(newCell);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2abdd57662eb8dc6e7fc8e6251294329acc199fc..90cb10aa62b2053152bb76f3ec90045d13e8f458 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -108,6 +108,7 @@ cc_library_shared_for_libandroid_runtime {
"libtracing_perfetto",
"libharfbuzz_ng",
"liblog",
+ "libmediautils",
"libminikin",
"libz",
"server_configurable_flags",
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 638591f130abdd17490b8e43d821d56e7fc7aaef..9bccf5af709670bed1c2de37b5fb39b83da0999d 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
+#include
#define LOG_TAG "AudioSystem-JNI"
#include
#include
@@ -34,15 +35,16 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
+#include
#include
-#include
#include
#include
#include
@@ -57,6 +59,7 @@
#include "android_media_AudioMixerAttributes.h"
#include "android_media_AudioProfile.h"
#include "android_media_MicrophoneInfo.h"
+#include "android_media_JNIUtils.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
@@ -3375,42 +3378,48 @@ static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIE
class JavaSystemPropertyListener {
public:
JavaSystemPropertyListener(JNIEnv* env, jobject javaCallback, std::string sysPropName) :
- mCallback(env->NewGlobalRef(javaCallback)),
- mCachedProperty(android::base::CachedProperty{std::move(sysPropName)}) {
- mListenerThread = std::thread([this]() mutable {
- JNIEnv* threadEnv = GetOrAttachJNIEnvironment(gVm);
- while (!mCleanupSignal.load()) {
- using namespace std::chrono_literals;
- // 1s timeout so this thread can read the cleanup signal to (slowly) be able to
- // be destroyed.
- std::string newVal = mCachedProperty.WaitForChange(1000ms) ?: "";
- if (newVal != "" && mLastVal != newVal) {
- threadEnv->CallVoidMethod(mCallback, gRunnableClassInfo.run);
- mLastVal = std::move(newVal);
+ mCallback {javaCallback, env},
+ mSysPropName(sysPropName),
+ mCachedProperty(android::base::CachedProperty{std::move(sysPropName)}),
+ mListenerThread([this](mediautils::stop_token stok) mutable {
+ while (!stok.stop_requested()) {
+ using namespace std::chrono_literals;
+ // 1s timeout so this thread can eventually respond to the stop token
+ std::string newVal = mCachedProperty.WaitForChange(1000ms) ?: "";
+ updateValue(newVal);
}
- }
- });
- }
+ }) {}
- ~JavaSystemPropertyListener() {
- mCleanupSignal.store(true);
- mListenerThread.join();
- JNIEnv* env = GetOrAttachJNIEnvironment(gVm);
- env->DeleteGlobalRef(mCallback);
+ void triggerUpdateIfChanged() {
+ // We must check the property without using the cached property due to thread safety issues
+ std::string newVal = base::GetProperty(mSysPropName, "");
+ updateValue(newVal);
}
private:
- jobject mCallback;
+ void updateValue(std::string newVal) {
+ if (newVal == "") return;
+ std::lock_guard l{mLock};
+ if (mLastVal == newVal) return;
+ const auto threadEnv = GetOrAttachJNIEnvironment(gVm);
+ threadEnv->CallVoidMethod(mCallback.get(), gRunnableClassInfo.run);
+ mLastVal = std::move(newVal);
+ }
+
+ // Should outlive thread object
+ const GlobalRef mCallback;
+ const std::string mSysPropName;
android::base::CachedProperty mCachedProperty;
- std::thread mListenerThread;
- std::atomic mCleanupSignal{false};
std::string mLastVal = "";
+ std::mutex mLock;
+ const mediautils::jthread mListenerThread;
};
+// A logical set keyed by address
std::vector> gSystemPropertyListeners;
std::mutex gSysPropLock{};
-static void android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env, jobject thiz,
+static jlong android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env, jobject thiz,
jstring sysProp,
jobject javaCallback) {
ScopedUtfChars sysPropChars{env, sysProp};
@@ -3418,6 +3427,19 @@ static void android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env,
std::string{sysPropChars.c_str()});
std::unique_lock _l{gSysPropLock};
gSystemPropertyListeners.push_back(std::move(listener));
+ return reinterpret_cast(gSystemPropertyListeners.back().get());
+}
+
+static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env, jobject thiz,
+ jlong nativeHandle) {
+ std::unique_lock _l{gSysPropLock};
+ const auto iter = std::find_if(gSystemPropertyListeners.begin(), gSystemPropertyListeners.end(),
+ [nativeHandle](const auto& x) { return reinterpret_cast(x.get()) == nativeHandle; });
+ if (iter != gSystemPropertyListeners.end()) {
+ (*iter)->triggerUpdateIfChanged();
+ } else {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid handle");
+ }
}
@@ -3595,8 +3617,11 @@ static const JNINativeMethod gMethods[] =
MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
- "(Ljava/lang/String;Ljava/lang/Runnable;)V",
+ "(Ljava/lang/String;Ljava/lang/Runnable;)J",
android_media_AudioSystem_listenForSystemPropertyChange),
+ MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate",
+ "(J)V",
+ android_media_AudioSystem_triggerSystemPropertyUpdate),
};
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 2068bd7bc8eabf097b6794224fb89e9719334306..921b77d61f4df28f8c05f834c76d387ae6d9ab16 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -56,11 +56,11 @@
//#undef ALOGV
//#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
-#define DEBUG_DEATH 0
-#if DEBUG_DEATH
-#define LOGDEATH ALOGD
+#define DEBUG_DEATH_FREEZE 0
+#if DEBUG_DEATH_FREEZE
+#define LOG_DEATH_FREEZE ALOGD
#else
-#define LOGDEATH ALOGV
+#define LOG_DEATH_FREEZE ALOGV
#endif
using namespace android;
@@ -116,6 +116,7 @@ static struct binderproxy_offsets_t
jclass mClass;
jmethodID mGetInstance;
jmethodID mSendDeathNotice;
+ jmethodID mInvokeFrozenStateChangeCallback;
// Object state.
jfieldID mNativeData; // Field holds native pointer to BinderProxyNativeData.
@@ -466,10 +467,25 @@ class JavaBBinderHolder
public:
sp get(JNIEnv* env, jobject obj)
{
- AutoMutex _l(mLock);
- sp b = mBinder.promote();
- if (b == NULL) {
- b = new JavaBBinder(env, obj);
+ sp b;
+ {
+ AutoMutex _l(mLock);
+ // must take lock to promote because we set the same wp<>
+ // on another thread.
+ b = mBinder.promote();
+ }
+
+ if (b) return b;
+
+ // b/360067751: constructor may trigger GC, so call outside lock
+ b = new JavaBBinder(env, obj);
+
+ {
+ AutoMutex _l(mLock);
+ // if it was constructed on another thread in the meantime,
+ // return that. 'b' will just get destructed.
+ if (sp b2 = mBinder.promote(); b2) return b2;
+
if (mVintf) {
::android::internal::Stability::markVintf(b.get());
}
@@ -532,23 +548,59 @@ private:
// ----------------------------------------------------------------------------
-// Per-IBinder death recipient bookkeeping. This is how we reconcile local jobject
-// death recipient references passed in through JNI with the permanent corresponding
-// JavaDeathRecipient objects.
-
-class JavaDeathRecipient;
-
-class DeathRecipientList : public RefBase {
- List< sp > mList;
+// A JavaRecipient receives either death notifications or frozen state change
+// callbacks from natve code (IBinder) and dispatch the notifications to its
+// corresponding Java listener object.
+//
+// A RecipientList keeps tracks of all JavaRecipients for an IBinder. This way
+// we can find a JavaRecipient given a Java listener object.
+//
+// The implementation is shared between death recipients and frozen state change
+// callbacks via template. For death recipients the template is instantiated as
+// follows:
+//
+// IBinder::DeathRecipient
+// ^
+// |
+// (inherits)
+// |
+// JavaRecipient <----> RecipientList
+// ^
+// |
+// (inherits)
+// |
+// JavaDeathRecipient
+//
+//
+// The instantiation for frozen state change callbacks are:
+//
+// IBinder::FrozenStateChangeCallback
+// ^
+// |
+// (inherits)
+// |
+// JavaRecipient
+// ^ ^
+// | |
+// (inherits) +--> RecipientList
+// |
+// JavaFrozenStateChangeCallback
+
+template
+class JavaRecipient;
+
+template
+class RecipientList : public RefBase {
+ List > > mList;
Mutex mLock;
public:
- DeathRecipientList();
- ~DeathRecipientList();
+ RecipientList();
+ ~RecipientList();
- void add(const sp& recipient);
- void remove(const sp& recipient);
- sp find(jobject recipient);
+ void add(const sp >& recipient);
+ void remove(const sp >& recipient);
+ sp > find(jobject recipient);
Mutex& lock(); // Use with care; specifically for mutual exclusion during binder death
};
@@ -569,11 +621,113 @@ static constexpr bool target_sdk_is_at_least_vic() {
#endif // __BIONIC__
#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
-class JavaDeathRecipient : public IBinder::DeathRecipient
-{
+template
+constexpr const char* logPrefix();
+
+template <>
+constexpr const char* logPrefix() {
+ return "[DEATH]";
+}
+
+template <>
+constexpr const char* logPrefix() {
+ return "[FREEZE]";
+}
+
+template
+class JavaRecipient : public T {
public:
- JavaDeathRecipient(JNIEnv* env, jobject object, const sp& list)
+ JavaRecipient(JNIEnv* env, jobject object, const sp >& list,
+ bool useWeakReference)
: mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) {
+ if (useWeakReference) {
+ mObjectWeak = env->NewWeakGlobalRef(object);
+ } else {
+ mObject = env->NewGlobalRef(object);
+ }
+ // These objects manage their own lifetimes so are responsible for final bookkeeping.
+ // The list holds a strong reference to this object.
+ LOG_DEATH_FREEZE("%s Adding JavaRecipient %p to RecipientList %p", logPrefix(), this,
+ list.get());
+ list->add(this);
+ }
+
+ void clearReference() {
+ sp > list = mList.promote();
+ if (list != NULL) {
+ LOG_DEATH_FREEZE("%s Removing JavaRecipient %p from RecipientList %p", logPrefix(),
+ this, list.get());
+ list->remove(this);
+ } else {
+ LOG_DEATH_FREEZE("%s clearReference() on JavaRecipient %p but RecipientList wp purged",
+ logPrefix(), this);
+ }
+ }
+
+ bool matches(jobject obj) {
+ bool result;
+ JNIEnv* env = javavm_to_jnienv(mVM);
+
+ if (mObject != NULL) {
+ result = env->IsSameObject(obj, mObject);
+ } else {
+ ScopedLocalRef me(env, env->NewLocalRef(mObjectWeak));
+ result = env->IsSameObject(obj, me.get());
+ }
+ return result;
+ }
+
+ void warnIfStillLive() {
+ if (mObject != NULL) {
+ // Okay, something is wrong -- we have a hard reference to a live death
+ // recipient on the VM side, but the list is being torn down.
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef objClassRef(env, env->GetObjectClass(mObject));
+ ScopedLocalRef nameRef(env,
+ (jstring)env->CallObjectMethod(objClassRef.get(),
+ gClassOffsets.mGetName));
+ ScopedUtfChars nameUtf(env, nameRef.get());
+ if (nameUtf.c_str() != NULL) {
+ ALOGW("BinderProxy is being destroyed but the application did not call "
+ "unlinkToDeath to unlink all of its death recipients beforehand. "
+ "Releasing leaked death recipient: %s",
+ nameUtf.c_str());
+ } else {
+ ALOGW("BinderProxy being destroyed; unable to get DR object name");
+ env->ExceptionClear();
+ }
+ }
+ }
+
+protected:
+ virtual ~JavaRecipient() {
+ // ALOGI("Removing death ref: recipient=%p\n", mObject);
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ if (mObject != NULL) {
+ env->DeleteGlobalRef(mObject);
+ } else {
+ env->DeleteWeakGlobalRef(mObjectWeak);
+ }
+ }
+
+ JavaVM* const mVM;
+
+ // If useWeakReference is false (e.g. JavaDeathRecipient when target sdk version < 35), the
+ // Java-side Recipient is strongly referenced from mObject initially, and may later be demoted
+ // to a weak reference (mObjectWeak), e.g. upon linkToDeath() and then after binderDied() is
+ // called.
+ // If useWeakReference is true, the strong reference is never made here (i.e. mObject == NULL
+ // always). Instead, the strong reference to the Java-side Recipient is made in
+ // BinderProxy.{mDeathRecipients,mFrozenStateChangeCallbacks}. In the native world, only the
+ // weak reference is kept.
+ jobject mObject;
+ jweak mObjectWeak;
+ wp > mList;
+};
+
+class JavaDeathRecipient : public JavaRecipient {
+public:
+ static bool useWeakReference() {
// b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref
// to the death recipient objects. This is to prevent the memory leak which can happen when
// the death recipient object internally has a strong reference to the proxy object. Under
@@ -589,25 +743,26 @@ public:
// reference to. If however you want to get binderDied() regardless of the proxy object's
// lifecycle, keep a strong reference to the death recipient object by yourself.
#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
- if (target_sdk_is_at_least_vic()) {
- mObjectWeak = env->NewWeakGlobalRef(object);
- } else
+ return target_sdk_is_at_least_vic();
+#else
+ return false;
#endif
- {
- mObject = env->NewGlobalRef(object);
- }
- // These objects manage their own lifetimes so are responsible for final bookkeeping.
- // The list holds a strong reference to this object.
- LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
- list->add(this);
+ }
+ JavaDeathRecipient(JNIEnv* env, jobject object,
+ const sp >& list)
+ : JavaRecipient(env, object, list, useWeakReference()) {
gNumDeathRefsCreated.fetch_add(1, std::memory_order_relaxed);
gcIfManyNewRefs(env);
}
+ ~JavaDeathRecipient() {
+ gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
+ }
+
void binderDied(const wp& who)
{
- LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
+ LOG_DEATH_FREEZE("Receiving binderDied() on JavaDeathRecipient %p\n", this);
if (mObject == NULL && mObjectWeak == NULL) {
return;
}
@@ -647,7 +802,7 @@ public:
// with our containing DeathRecipientList so that we can't delete the global ref on mObject
// while the list is being iterated.
if (mObject != NULL) {
- sp list = mList.promote();
+ auto list = mList.promote();
if (list != NULL) {
AutoMutex _l(list->lock());
@@ -658,126 +813,96 @@ public:
}
}
- void clearReference()
- {
- sp list = mList.promote();
- if (list != NULL) {
- LOGDEATH("Removing JDR %p from DRL %p", this, list.get());
- list->remove(this);
- } else {
- LOGDEATH("clearReference() on JDR %p but DRL wp purged", this);
- }
- }
-
- bool matches(jobject obj) {
- bool result;
- JNIEnv* env = javavm_to_jnienv(mVM);
+private:
+ // Whether binderDied was called or not.
+ bool mFired = false;
+};
- if (mObject != NULL) {
- result = env->IsSameObject(obj, mObject);
- } else {
- ScopedLocalRef me(env, env->NewLocalRef(mObjectWeak));
- result = env->IsSameObject(obj, me.get());
+class JavaFrozenStateChangeCallback : public JavaRecipient {
+public:
+ JavaFrozenStateChangeCallback(
+ JNIEnv* env, jobject object,
+ const sp >& list)
+ : JavaRecipient(env, object, list, /*useWeakReference=*/true) {}
+
+ void onStateChanged(const wp& who, State state) {
+ LOG_DEATH_FREEZE("Receiving onStateChanged() on JavaFrozenStateChangeCallback %p. state: "
+ "%s\n",
+ this, state == State::FROZEN ? "FROZEN" : "UNFROZEN");
+ if (mObjectWeak == NULL) {
+ return;
}
- return result;
- }
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
- void warnIfStillLive() {
- if (mObject != NULL) {
- // Okay, something is wrong -- we have a hard reference to a live death
- // recipient on the VM side, but the list is being torn down.
- JNIEnv* env = javavm_to_jnienv(mVM);
- ScopedLocalRef objClassRef(env, env->GetObjectClass(mObject));
- ScopedLocalRef nameRef(env,
- (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
- ScopedUtfChars nameUtf(env, nameRef.get());
- if (nameUtf.c_str() != NULL) {
- ALOGW("BinderProxy is being destroyed but the application did not call "
- "unlinkToDeath to unlink all of its death recipients beforehand. "
- "Releasing leaked death recipient: %s", nameUtf.c_str());
- } else {
- ALOGW("BinderProxy being destroyed; unable to get DR object name");
- env->ExceptionClear();
- }
+ // Hold a local reference to the recipient. This may fail if the recipient is weakly
+ // referenced, in which case we can't deliver the notification.
+ ScopedLocalRef jCallback(env, env->NewLocalRef(mObjectWeak));
+ if (jCallback.get() == NULL) {
+ return;
}
- }
-
-protected:
- virtual ~JavaDeathRecipient()
- {
- //ALOGI("Removing death ref: recipient=%p\n", mObject);
- gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
- JNIEnv* env = javavm_to_jnienv(mVM);
- if (mObject != NULL) {
- env->DeleteGlobalRef(mObject);
- } else {
- env->DeleteWeakGlobalRef(mObjectWeak);
+ env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
+ gBinderProxyOffsets.mInvokeFrozenStateChangeCallback,
+ jCallback.get(), jBinderProxy.get(), state);
+ if (env->ExceptionCheck()) {
+ jthrowable excep = env->ExceptionOccurred();
+ binder_report_exception(env, excep,
+ "*** Uncaught exception returned from frozen state change "
+ "notification!");
}
}
-
-private:
- JavaVM* const mVM;
-
- // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject
- // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to
- // a weak reference (mObjectWeak).
- // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL
- // always). Instead, the strong reference to the Java-side DeathRecipient is made in
- // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept.
- jobject mObject;
- jweak mObjectWeak;
- wp mList;
-
- // Whether binderDied was called or not.
- bool mFired = false;
};
// ----------------------------------------------------------------------------
-DeathRecipientList::DeathRecipientList() {
- LOGDEATH("New DRL @ %p", this);
+template
+RecipientList::RecipientList() {
+ LOG_DEATH_FREEZE("%s New RecipientList @ %p", logPrefix(), this);
}
-DeathRecipientList::~DeathRecipientList() {
- LOGDEATH("Destroy DRL @ %p", this);
+template
+RecipientList::~RecipientList() {
+ LOG_DEATH_FREEZE("%s Destroy RecipientList @ %p", logPrefix(), this);
AutoMutex _l(mLock);
- // Should never happen -- the JavaDeathRecipient objects that have added themselves
+ // Should never happen -- the JavaRecipientList objects that have added themselves
// to the list are holding references on the list object. Only when they are torn
// down can the list header be destroyed.
if (mList.size() > 0) {
- List< sp >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
(*iter)->warnIfStillLive();
}
}
}
-void DeathRecipientList::add(const sp& recipient) {
+template
+void RecipientList::add(const sp >& recipient) {
AutoMutex _l(mLock);
- LOGDEATH("DRL @ %p : add JDR %p", this, recipient.get());
+ LOG_DEATH_FREEZE("%s RecipientList @ %p : add JavaRecipient %p", logPrefix(), this,
+ recipient.get());
mList.push_back(recipient);
}
-void DeathRecipientList::remove(const sp& recipient) {
+template
+void RecipientList::remove(const sp >& recipient) {
AutoMutex _l(mLock);
- List< sp >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
if (*iter == recipient) {
- LOGDEATH("DRL @ %p : remove JDR %p", this, recipient.get());
+ LOG_DEATH_FREEZE("%s RecipientList @ %p : remove JavaRecipient %p", logPrefix(),
+ this, recipient.get());
mList.erase(iter);
return;
}
}
}
-sp DeathRecipientList::find(jobject recipient) {
+template
+sp > RecipientList::find(jobject recipient) {
AutoMutex _l(mLock);
- List< sp >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
if ((*iter)->matches(recipient)) {
return *iter;
}
@@ -785,10 +910,14 @@ sp DeathRecipientList::find(jobject recipient) {
return NULL;
}
-Mutex& DeathRecipientList::lock() {
+template
+Mutex& RecipientList::lock() {
return mLock;
}
+using DeathRecipientList = RecipientList;
+using FrozenStateChangeCallbackList = RecipientList;
+
// ----------------------------------------------------------------------------
namespace android {
@@ -806,6 +935,11 @@ struct BinderProxyNativeData {
// Death recipients for mObject. Reference counted only because DeathRecipients
// hold a weak reference that can be temporarily promoted.
sp mOrgue; // Death recipients for mObject.
+
+ // Frozen state change callbacks for mObject. Reference counted only because
+ // JavaFrozenStateChangeCallback hold a weak reference that can be
+ // temporarily promoted.
+ sp mFrozenStateChangCallbackList;
};
BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
@@ -825,12 +959,13 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp& val)
if (val->checkSubclass(&gBinderOffsets)) {
// It's a JavaBBinder created by ibinderForJavaObject. Already has Java object.
jobject object = static_cast(val.get())->object();
- LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
+ LOG_DEATH_FREEZE("objectForBinder %p: it's our own %p!\n", val.get(), object);
return object;
}
BinderProxyNativeData* nativeData = new BinderProxyNativeData();
nativeData->mOrgue = new DeathRecipientList;
+ nativeData->mFrozenStateChangCallbackList = new FrozenStateChangeCallbackList;
nativeData->mObject = val;
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
@@ -1433,7 +1568,7 @@ static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
BinderProxyNativeData *nd = getBPNativeData(env, obj);
IBinder* target = nd->mObject.get();
- LOGDEATH("linkToDeath: binder=%p recipient=%p\n", target, recipient);
+ LOG_DEATH_FREEZE("linkToDeath: binder=%p recipient=%p\n", target, recipient);
if (!target->localBinder()) {
DeathRecipientList* list = nd->mOrgue.get();
@@ -1464,15 +1599,15 @@ static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj,
return JNI_FALSE;
}
- LOGDEATH("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
+ LOG_DEATH_FREEZE("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
if (!target->localBinder()) {
status_t err = NAME_NOT_FOUND;
// If we find the matching recipient, proceed to unlink using that
DeathRecipientList* list = nd->mOrgue.get();
- sp origJDR = list->find(recipient);
- LOGDEATH(" unlink found list %p and JDR %p", list, origJDR.get());
+ sp > origJDR = list->find(recipient);
+ LOG_DEATH_FREEZE(" unlink found list %p and JDR %p", list, origJDR.get());
if (origJDR != NULL) {
wp dr;
err = target->unlinkToDeath(origJDR, NULL, flags, &dr);
@@ -1498,11 +1633,85 @@ static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj,
return res;
}
+static void android_os_BinderProxy_addFrozenStateChangeCallback(
+ JNIEnv* env, jobject obj,
+ jobject callback) // throws RemoteException
+{
+ if (callback == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return;
+ }
+
+ BinderProxyNativeData* nd = getBPNativeData(env, obj);
+ IBinder* target = nd->mObject.get();
+
+ LOG_DEATH_FREEZE("addFrozenStateChangeCallback: binder=%p callback=%p\n", target, callback);
+
+ if (!target->localBinder()) {
+ FrozenStateChangeCallbackList* list = nd->mFrozenStateChangCallbackList.get();
+ auto jfscc = sp::make(env, callback, list);
+ status_t err = target->addFrozenStateChangeCallback(jfscc);
+ if (err != NO_ERROR) {
+ // Failure adding the callback, so clear its reference now.
+ jfscc->clearReference();
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
+ }
+ }
+}
+
+static jboolean android_os_BinderProxy_removeFrozenStateChangeCallback(JNIEnv* env, jobject obj,
+ jobject callback) {
+ jboolean res = JNI_FALSE;
+ if (callback == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return res;
+ }
+
+ BinderProxyNativeData* nd = getBPNativeData(env, obj);
+ IBinder* target = nd->mObject.get();
+ if (target == NULL) {
+ ALOGW("Binder has been finalized when calling removeFrozenStateChangeCallback() with "
+ "callback=%p)\n",
+ callback);
+ return JNI_FALSE;
+ }
+
+ LOG_DEATH_FREEZE("removeFrozenStateChangeCallback: binder=%p callback=%p\n", target, callback);
+
+ if (!target->localBinder()) {
+ status_t err = NAME_NOT_FOUND;
+
+ // If we find the matching callback, proceed to unlink using that
+ FrozenStateChangeCallbackList* list = nd->mFrozenStateChangCallbackList.get();
+ sp > origJFSCC = list->find(callback);
+ LOG_DEATH_FREEZE(" removeFrozenStateChangeCallback found list %p and JFSCC %p", list,
+ origJFSCC.get());
+ if (origJFSCC != NULL) {
+ err = target->removeFrozenStateChangeCallback(origJFSCC);
+ if (err == NO_ERROR) {
+ origJFSCC->clearReference();
+ }
+ }
+
+ if (err == NO_ERROR || err == DEAD_OBJECT) {
+ res = JNI_TRUE;
+ } else {
+ jniThrowException(env, "java/util/NoSuchElementException",
+ base::StringPrintf("Frozen state change callback does not exist (%s)",
+ statusToString(err).c_str())
+ .c_str());
+ }
+ }
+
+ return res;
+}
+
static void BinderProxy_destroy(void* rawNativeData)
{
BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
- LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
- nativeData->mObject.get(), nativeData->mOrgue.get());
+ LOG_DEATH_FREEZE("Destroying BinderProxy: binder=%p drl=%p fsccl=%p\n",
+ nativeData->mObject.get(), nativeData->mOrgue.get(),
+ nativeData->mFrozenStateChangCallbackList.get());
delete nativeData;
IPCThreadState::self()->flushCommands();
}
@@ -1537,6 +1746,10 @@ static const JNINativeMethod gBinderProxyMethods[] = {
{"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
{"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
{"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+ {"addFrozenStateChangeCallbackNative",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;)V", (void*)android_os_BinderProxy_addFrozenStateChangeCallback},
+ {"removeFrozenStateChangeCallbackNative",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;)Z", (void*)android_os_BinderProxy_removeFrozenStateChangeCallback},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
{"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
@@ -1559,6 +1772,10 @@ static int int_register_android_os_BinderProxy(JNIEnv* env)
gBinderProxyOffsets.mSendDeathNotice =
GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
+ gBinderProxyOffsets.mInvokeFrozenStateChangeCallback =
+ GetStaticMethodIDOrDie(env, clazz, "invokeFrozenStateChangeCallback",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;Landroid/os/"
+ "IBinder;I)V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0f531641903af0ad4916b7edad0f26d547c5e20b..17c89f88b4411d16b9fd2471a9d691fb5e7c35e2 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -2089,9 +2089,11 @@ public:
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
for (size_t i = 0; i < jankData.size(); i++) {
- jobject jJankData = env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
- jankData[i].frameVsyncId, jankData[i].jankType,
- jankData[i].frameIntervalNs);
+ jobject jJankData =
+ env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
+ jankData[i].frameVsyncId, jankData[i].jankType,
+ jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs,
+ jankData[i].actualAppFrameTimeNs);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
env->DeleteLocalRef(jJankData);
}
@@ -2727,7 +2729,7 @@ int register_android_view_SurfaceControl(JNIEnv* env)
jclass jankDataClazz =
FindClassOrDie(env, "android/view/SurfaceControl$JankData");
gJankDataClassInfo.clazz = MakeGlobalRefOrDie(env, jankDataClazz);
- gJankDataClassInfo.ctor = GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "", "(JIJ)V");
+ gJankDataClassInfo.ctor = GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "", "(JIJJJ)V");
jclass onJankDataListenerClazz =
FindClassOrDie(env, "android/view/SurfaceControl$OnJankDataListener");
gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz);
diff --git a/core/jni/android_view_TunnelModeEnabledListener.cpp b/core/jni/android_view_TunnelModeEnabledListener.cpp
index af7bae8c89ddba7943cff8e9ff87d64d94e02949..d9ab9571cfbe9b151e5a11517792859d4b1eeca9 100644
--- a/core/jni/android_view_TunnelModeEnabledListener.cpp
+++ b/core/jni/android_view_TunnelModeEnabledListener.cpp
@@ -88,20 +88,19 @@ void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr) {
sp listener = reinterpret_cast(ptr);
- if (SurfaceComposerClient::addTunnelModeEnabledListener(listener) != OK) {
- constexpr auto error_msg = "Couldn't addTunnelModeEnabledListener";
- ALOGE(error_msg);
- jniThrowRuntimeException(env, error_msg);
+ status_t status = SurfaceComposerClient::addTunnelModeEnabledListener(listener);
+ if (status != OK) {
+ ALOGE("Couldn't addTunnelModeEnabledListener (%d)", status);
+ jniThrowRuntimeException(env, "Couldn't addTunnelModeEnabledListener");
}
}
void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
sp listener = reinterpret_cast(ptr);
-
- if (SurfaceComposerClient::removeTunnelModeEnabledListener(listener) != OK) {
- constexpr auto error_msg = "Couldn't removeTunnelModeEnabledListener";
- ALOGE(error_msg);
- jniThrowRuntimeException(env, error_msg);
+ status_t status = SurfaceComposerClient::removeTunnelModeEnabledListener(listener);
+ if (status != OK) {
+ ALOGE("Couldn't removeTunnelModeEnabledListener (%d)", status);
+ jniThrowRuntimeException(env, "Couldn't removeTunnelModeEnabledListener");
}
}
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index fba0d81d431f42b5c3d4f3f2914f4bf9297d3b4b..7ad18b83f0d6b0cbb25eb97b2338e4bfe368508b 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "NativeLibraryHelper"
//#define LOG_NDEBUG 0
+#include
#include
#include
#include
@@ -36,6 +37,7 @@
#include
#include
+#include
#include "com_android_internal_content_FileSystemUtils.h"
#include "core_jni_helpers.h"
@@ -125,72 +127,10 @@ sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char
return INSTALL_SUCCEEDED;
}
-/*
- * Copy the native library if needed.
- *
- * This function assumes the library and path names passed in are considered safe.
- */
-static install_status_t
-copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
-{
- static const size_t kPageSize = getpagesize();
- void** args = reinterpret_cast(arg);
- jstring* javaNativeLibPath = (jstring*) args[0];
- jboolean extractNativeLibs = *(jboolean*) args[1];
- jboolean debuggable = *(jboolean*) args[2];
-
- ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
-
- uint32_t uncompLen;
- uint32_t when;
- uint32_t crc;
-
- uint16_t method;
- off64_t offset;
- uint16_t extraFieldLength;
- if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
- &extraFieldLength)) {
- ALOGE("Couldn't read zip entry info\n");
- return INSTALL_FAILED_INVALID_APK;
- }
-
- // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
- // easier to use wrap.sh because it only works when it is extracted, see
- // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
- bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;
-
- if (!extractNativeLibs && !forceExtractCurrentFile) {
- // check if library is uncompressed and page-aligned
- if (method != ZipFileRO::kCompressStored) {
- ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
- fileName);
- return INSTALL_FAILED_INVALID_APK;
- }
-
- if (offset % kPageSize != 0) {
- ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
- "from apk.\n", fileName, kPageSize);
- return INSTALL_FAILED_INVALID_APK;
- }
-
-#ifdef ENABLE_PUNCH_HOLES
- // if library is uncompressed, punch hole in it in place
- if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
- ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
- "%" PRIu64 "",
- fileName, zipFile->getZipFileName(), offset);
- }
-
- // if extra field for this zip file is present with some length, possibility is that it is
- // padding added for zip alignment. Punch holes there too.
- if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
- ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
- }
-#endif // ENABLE_PUNCH_HOLES
-
- return INSTALL_SUCCEEDED;
- }
-
+static install_status_t extractNativeLibFromApk(ZipFileRO* zipFile, ZipEntryRO zipEntry,
+ const char* fileName,
+ const std::string nativeLibPath, uint32_t when,
+ uint32_t uncompLen, uint32_t crc) {
// Build local file path
const size_t fileNameLen = strlen(fileName);
char localFileName[nativeLibPath.size() + fileNameLen + 2];
@@ -312,6 +252,88 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr
return INSTALL_SUCCEEDED;
}
+/*
+ * Copy the native library if needed.
+ *
+ * This function assumes the library and path names passed in are considered safe.
+ */
+static install_status_t copyFileIfChanged(JNIEnv* env, void* arg, ZipFileRO* zipFile,
+ ZipEntryRO zipEntry, const char* fileName) {
+ static const size_t kPageSize = getpagesize();
+ void** args = reinterpret_cast(arg);
+ jstring* javaNativeLibPath = (jstring*)args[0];
+ jboolean extractNativeLibs = *(jboolean*)args[1];
+ jboolean debuggable = *(jboolean*)args[2];
+ jboolean app_compat_16kb = *(jboolean*)args[3];
+ install_status_t ret = INSTALL_SUCCEEDED;
+
+ ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
+
+ uint32_t uncompLen;
+ uint32_t when;
+ uint32_t crc;
+
+ uint16_t method;
+ off64_t offset;
+ uint16_t extraFieldLength;
+ if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
+ &extraFieldLength)) {
+ ALOGE("Couldn't read zip entry info\n");
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
+ // easier to use wrap.sh because it only works when it is extracted, see
+ // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
+ bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;
+
+ if (!extractNativeLibs && !forceExtractCurrentFile) {
+ // check if library is uncompressed and page-aligned
+ if (method != ZipFileRO::kCompressStored) {
+ ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+ fileName);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ if (offset % kPageSize != 0) {
+ // If the library is zip-aligned correctly for 4kb devices and app compat is
+ // enabled, on 16kb devices fallback to extraction
+ if (offset % 0x1000 == 0 && app_compat_16kb) {
+ ALOGI("16kB AppCompat: Library '%s' is not PAGE(%zu)-aligned - falling back to "
+ "extraction from apk\n",
+ fileName, kPageSize);
+ return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(),
+ when, uncompLen, crc);
+ }
+
+ ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
+ "from apk.\n",
+ fileName, kPageSize);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+#ifdef ENABLE_PUNCH_HOLES
+ // if library is uncompressed, punch hole in it in place
+ if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
+ ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
+ "%" PRIu64 "",
+ fileName, zipFile->getZipFileName(), offset);
+ }
+
+ // if extra field for this zip file is present with some length, possibility is that it is
+ // padding added for zip alignment. Punch holes there too.
+ if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
+ ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
+ }
+#endif // ENABLE_PUNCH_HOLES
+
+ return INSTALL_SUCCEEDED;
+ }
+
+ return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(), when,
+ uncompLen, crc);
+}
+
/*
* An iterator over all shared libraries in a zip file. An entry is
* considered to be a shared library if all of the conditions below are
@@ -498,12 +520,24 @@ static int findSupportedAbi(JNIEnv* env, jlong apkHandle, jobjectArray supported
return status;
}
+static inline bool app_compat_16kb_enabled() {
+ static const size_t kPageSize = getpagesize();
+
+ // App compat is only applicable on 16kb-page-size devices.
+ if (kPageSize != 0x4000) {
+ return false;
+ }
+
+ return android::base::GetBoolProperty("bionic.linker.16kb.app_compat.enabled", false);
+}
+
static jint
com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
jboolean extractNativeLibs, jboolean debuggable)
{
- void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable };
+ jboolean app_compat_16kb = app_compat_16kb_enabled();
+ void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable, &app_compat_16kb };
return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable,
copyFileIfChanged, reinterpret_cast(args));
}
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 020b27e82bea1a26d4a547432ddbf74a5c07e7ca..19f82998c1a34797bbf8d399c0b7c5511b68bd6f 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -391,6 +391,7 @@ public:
} // namespace android
+#ifndef _WIN32
using namespace android;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
@@ -407,3 +408,4 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
return JNI_VERSION_1_6;
}
+#endif
diff --git a/core/res/Android.bp b/core/res/Android.bp
index bcc0a975b913468c77403f0bdb92091c9310d554..17d7bfa40f9059ba425b2f895b3fcfff1af6aec8 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -167,6 +167,7 @@ android_app {
"android.os.flags-aconfig",
"android.os.vibrator.flags-aconfig",
"android.media.tv.flags-aconfig",
+ "com.android.hardware.input.input-aconfig",
],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 91c33702d3e3abf4e1252d6a8063643f8d19a8c1..d35c66ed719ef4cc200dc7e17f244f801ca01c22 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -843,6 +843,7 @@
+
@@ -3765,7 +3766,6 @@
privileged app such as the Assistant app.
Protection level: internal|role
Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled")
-->
@@ -4026,7 +4026,6 @@
APIs protected by this permission on users different to the calling user.
Protection level: internal|role
Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.esim_management_enabled")
-->
@@ -4034,7 +4033,6 @@
@@ -4042,7 +4040,6 @@
@@ -4050,7 +4047,6 @@
@@ -7649,7 +7645,8 @@
-
@@ -8171,7 +8168,8 @@
Not for use by third-party applications.
@hide -->
+ android:protectionLevel="signature"
+ android:featureFlag="com.android.hardware.input.manage_key_gestures" />
diff --git a/core/res/res/drawable/ic_zen_mode_type_special_dnd.xml b/core/res/res/drawable/ic_zen_mode_type_special_dnd.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fee9b0e2f4519412c066c3fd53ec7513129d49af
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_special_dnd.xml
@@ -0,0 +1,25 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_priority_modes.xml b/core/res/res/drawable/ic_zen_priority_modes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9c72f518db8d8af82be1c483bc87fe803163c7db
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_priority_modes.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer.xml b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
index 4d5f4f09d29e1219ecaf54bdbf6d141c0f05b47c..027f530ab648e11fbadf23f3997a038200e011fa 100644
--- a/core/res/res/layout/autofill_dataset_picker_header_footer.xml
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
@@ -37,6 +37,7 @@
"Terug"
"Wissel invoermetode"
"Maak invoermetodekieser oop"
-
-
+ "Instellings"
"Bergingspasie word min"
"Sommige stelselfunksies werk moontlik nie"
"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."
@@ -1747,7 +1746,8 @@
"’n App verberg die toestemmingversoek en jou antwoord kan dus nie geverifieer word nie."
"Tik op \'n kenmerk om dit te begin gebruik:"
"Kies kenmerke om saam met die toeganklikheidknoppie te gebruik"
- "Kies kenmerke om saam met die volumesleutelkortpad te gebruik"
+
+
"%s is afgeskakel"
"Wysig kortpaaie"
"Klaar"
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index d97c903ce13409c2d63363d20acb58c7332fff32..2873785e9387f5fc9e6a60e8f613bd80abf3f61d 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1195,8 +1195,7 @@
"ተመለስ"
"የግቤት ስልትን ቀይር"
"የግቤት ስልት መራጭን ክፈት"
-
-
+ "ቅንብሮች"
"የማከማቻ ቦታ እያለቀ ነው"
"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"
"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነፃ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"
@@ -1747,7 +1746,8 @@
"አንድ መተግበሪያ የፍቃድ ጥያቄውን እያደበዘዘ ነው ስለዚህ የእርስዎ ምላሽ ሊረጋገጥ አይችልም።"
"አንድ ባህሪን መጠቀም ለመጀመር መታ ያድርጉት፦"
"በተደራሽነት አዝራር የሚጠቀሙባቸው ባሕሪያት ይምረጡ"
- "በድምጽ ቁልፍ አቋራጭ የሚጠቀሙባቸው ባሕሪያት ይምረጡ"
+
+
"%s ጠፍቷል"
"አቋራጮችን አርትዕ ያድርጉ"
"ተከናውኗል"
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index c7983411fcc19ff4752a6db367eaeb8011c1e64d..7f025760466bbd14b9b1897aa45a1a7d644b5f8e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1199,8 +1199,7 @@
"رجوع"
"تبديل أسلوب الإدخال"
"فتح أداة اختيار أسلوب الإدخال"
-
-
+ "الإعدادات"
"مساحة التخزين منخفضة"
"قد لا تعمل بعض وظائف النظام"
"ليست هناك مساحة تخزين كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."
@@ -1751,7 +1750,8 @@
"تعذَّر التحقّق من ردّك بسبب حجب أحد التطبيقات طلب الحصول على الإذن."
"انقر على ميزة لبدء استخدامها:"
"اختيار الميزات التي تريد استخدامها مع زر أدوات تمكين الوصول"
- "اختيار الميزات التي تريد استخدامها مع اختصار مفتاح التحكّم في مستوى الصوت"
+
+
"تم إيقاف %s."
"تعديل الاختصارات"
"تم"
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index af97d436310583151652977f5914d7d8fdf54ba8..bc1dba27ff5ff3cf175fbb7a5596998998f2025a 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1195,8 +1195,7 @@
"উভতি যাওক"
"ইনপুটৰ পদ্ধতি সলনি কৰক"
"ইনপুট পদ্ধতি বাছনিকর্তা খোলক"
-
-
+ "ছেটিং"
"ষ্ট’ৰেজৰ খালী ঠাই শেষ হৈ আছে"
"ছিষ্টেমৰ কিছুমান কাৰ্যকলাপে কাম নকৰিবও পাৰে"
"ছিষ্টেমৰ বাবে পৰ্যাপ্ত খালী ঠাই নাই। আপোনাৰ ২৫০এমবি খালী ঠাই থকাটো নিশ্চিত কৰক আৰু ৰিষ্টাৰ্ট কৰক।"
@@ -1747,7 +1746,8 @@
"এটা এপে অনুমতিৰ অনুৰোধটো অস্পষ্ট কৰি আছে আৰু সেয়েহে আপোনাৰ সঁহাৰিটো সত্যাপন কৰিব নোৱাৰি।"
"কোনো এটা সুবিধা ব্যৱহাৰ কৰিবলৈ সেইটোত টিপক:"
"সাধ্য-সুবিধা বুটামটোৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ সুবিধাসমূহ বাছনি কৰক"
- "ভলিউম কীৰ শ্বৰ্টকাটটোৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ সুবিধাসমূহ বাছনি কৰক"
+
+
"%s বন্ধ কৰা হৈছে"
"শ্বৰ্টকাটসমূহ সম্পাদনা কৰক"
"কৰা হ’ল"
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 72daeaffd777d2f7b61a4ac9bd393f8e4954e97e..e93a57ba76ccc7dc9ed2800688ed79317e75d599 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1195,8 +1195,7 @@
"Geriyə"
"Daxiletmə metodunu dəyişdirin"
"Daxiletmə metodu seçicisini açın"
-
-
+ "Ayarlar"
"Yaddaş yeri bitir"
"Bəzi sistem funksiyaları işləməyə bilər"
"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."
@@ -1747,7 +1746,8 @@
"Bir tətbiq icazə sorğusunu gizlətdiyi üçün cavabı yoxlamaq mümkün deyil."
"Funksiyanı istifadə etmək üçün onun üzərinə toxunun:"
"Xüsusi imkanlar düyməsinin köməyilə işə salınacaq funksiyaları seçin"
- "Səs səviyyəsi düyməsinin qısayolu ilə istifadə edəcəyiniz funksiyaları seçin"
+
+
"%s deaktiv edilib"
"Qısayolları redaktə edin"
"Hazırdır"
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 917df575c1a2b1e0a9cd7495c04cdff8c9f72829..b83eca56023fb952449a6f10b4dc29f2f28038b7 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1196,8 +1196,7 @@
"Nazad"
"Promenite metod unosa"
"Otvori birač metoda unosa"
-
-
+ "Podešavanja"
"Memorijski prostor je na izmaku"
"Neke sistemske funkcije možda ne funkcionišu"
"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."
@@ -1748,7 +1747,8 @@
"Aplikacija krije zahtev za dozvolu, pa odgovor ne može da se verifikuje."
"Dodirnite neku funkciju da biste počeli da je koristite:"
"Odaberite funkcije koje ćete koristiti sa dugmetom Pristupačnost"
- "Odaberite funkcije za prečicu tasterom jačine zvuka"
+
+
"Usluga %s je isključena"
"Izmenite prečice"
"Gotovo"
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 86e81d1d9a0340ccc45825545930560ec72bb96e..5964570662685ea489588ebfd3c93c2381c46920 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1197,8 +1197,7 @@
"Назад"
"Пераключэнне рэжыму ўводу"
"Выбраць спосаб уводу"
-
-
+ "Налады"
"Месца для захавання на зыходзе"
"Некаторыя сістэмныя функцыі могуць не працаваць"
"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."
@@ -1749,7 +1748,8 @@
"Праграма хавае запыт дазволу, таму ваш адказ немагчыма спраўдзіць."
"Каб пачаць выкарыстоўваць функцыю, націсніце на яе:"
"Выберыце функцыі, якія будзеце выкарыстоўваць з кнопкай спецыяльных магчымасцей"
- "Выберыце функцыі для выкарыстання з клавішай гучнасці"
+
+
"Сэрвіс \"%s\" выключаны"
"Змяніць ярлыкі"
"Гатова"
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 9f869469d9bc0580c404f33cf32f5c69eb84d3b6..b6fad9e9a848ef1f123f7ad4bab95c842a6c002a 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1195,8 +1195,7 @@
"Назад"
"Превключване на метода на въвеждане"
"Отваряне на инструмента за избор на метод на въвеждане"
-
-
+ "Настройки"
"Мястото в хранилището е на изчерпване"
"Възможно е някои функции на системата да не работят"
"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."
@@ -1747,7 +1746,8 @@
"Отговорът ви не може да бъде потвърден, тъй като приложение прикрива заявката за разрешение."
"Докоснете дадена функция, за да започнете да я използвате:"
"Избиране на функции, които да използвате с бутона за достъпност"
- "Избиране на функции, които да използвате с прекия път чрез бутона за силата на звука"
+
+
"Изключихте %s"
"Редактиране на преките пътища"
"Готово"
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 9bcefa7c067e687ed2b0da73a0b8b97c5c15e48e..c434fe91d11e726c24440c76aec679762eb8ad47 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1195,8 +1195,7 @@
"ফিরে যান"
"ইনপুট পদ্ধতি পাল্টান"
"ইনপুট পদ্ধতির পিকার খুলুন"
-
-
+ "সেটিংস"
"স্টোরেজ পূর্ণ হতে চলেছে"
"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"
"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"
@@ -1747,7 +1746,8 @@
"কোনও অ্যাপ অনুমতির অনুরোধ আড়াল করছে তাই আপনার উত্তর যাচাই করা যাবে না।"
"কোনও ফিচার ব্যবহার করা শুরু করতে, সেটিতে ট্যাপ করুন:"
"অ্যাক্সেসিবিলিটি বোতামের সাহায্যে আপনি যেসব ফিচার ব্যবহার করতে চান সেগুলি বেছে নিন"
- "ভলিউম কী শর্টকাটের সাহায্যে আপনি যেসব ফিচার ব্যবহার করতে চান সেগুলি বেছে নিন"
+
+
"%s বন্ধ করে দেওয়া হয়েছে"
"শর্টকাট এডিট করুন"
"হয়ে গেছে"
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 017e8b4116a9d660e37d3003ee61d045f1fa61c4..91c77018810d61dd026299f6c7664ce12b442d39 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1196,8 +1196,7 @@
"Nazad"
"Promjena načina unosa"
"Otvaranje birača načina unosa"
-
-
+ "Postavke"
"Ponestaje prostora za pohranu"
"Neke funkcije sistema možda neće raditi"
"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."
@@ -1748,7 +1747,8 @@
"Aplikacija skriva zahtjev za odobrenje, pa se vaš odgovor ne može potvrditi."
"Dodirnite funkciju da je počnete koristiti:"
"Odaberite funkcije koje ćete koristiti s dugmetom Pristupačnost"
- "Odaberite funkcije koje ćete koristiti pomoću prečice tipke za jačinu zvuka"
+
+
"Usluga %s je isključena"
"Uredi prečice"
"Gotovo"
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e3b373415d91dff4999ffb724c953c762465c246..cbe9c392772600414101ab49077d465dbd62f989 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1196,8 +1196,7 @@
"Enrere"
"Canvia el mètode d\'introducció de text"
"Obre el selector de mètode d\'introducció"
-
-
+ "Configuració"
"L\'espai d\'emmagatzematge s\'està esgotant"
"És possible que algunes funcions del sistema no funcionin"
"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."
@@ -1748,7 +1747,8 @@
"Una aplicació està ocultant la sol·licitud de permís, de manera que la teva resposta no es pot verificar"
"Toca una funció per començar a utilitzar-la:"
"Tria les funcions que vols utilitzar amb el botó d\'accessibilitat"
- "Tria les funcions que vols utilitzar amb la drecera per a tecles de volum"
+
+
"%s s\'ha desactivat"
"Edita les dreceres"
"Fet"
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 5dde261ac56ed969a43ab009e284be561a49b9f3..b9ceede3c8be3b5c82072ad27b1d351697ec9e40 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1197,8 +1197,7 @@
"Zpět"
"Přepnout metodu zadávání"
"Otevřít výběr metody zadávání"
-
-
+ "Nastavení"
"V úložišti je málo místa"
"Některé systémové funkce nemusí fungovat"
"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."
@@ -1749,7 +1748,8 @@
"Žádost o oprávnění skrývá nějaká aplikace, proto vaši odpověď nelze ověřit."
"Chcete-li některou funkci začít používat, klepněte na ni:"
"Vyberte funkce, které budete používat s tlačítkem přístupnosti"
- "Vyberte funkce, které budete používat se zkratkou tlačítka hlasitosti"
+
+
"Služba %s byla vypnuta"
"Upravit zkratky"
"Hotovo"
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 00fc1eb684d9f2eb97d1fcd36c75a7a8f97b0975..d66ebd90723720a429ad349da0222a7ac6f0b0e6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1195,8 +1195,7 @@
"Tilbage"
"Skift indtastningsmetode"
"Åbn indtastningsmetodevælgeren"
-
-
+ "Indstillinger"
"Der er snart ikke mere lagerplads"
"Nogle systemfunktioner virker måske ikke"
"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."
@@ -1747,7 +1746,8 @@
"En app skjuler anmodningen om tilladelse, så dit svar kan ikke verificeres."
"Tryk på en funktion for at bruge den:"
"Vælg, hvilke funktioner du vil bruge med knappen til hjælpefunktioner"
- "Vælg de funktioner, du vil bruge via lydstyrkeknapperne"
+
+
"%s er blevet deaktiveret"
"Rediger genveje"
"Udfør"
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index bc35788159de754776dbc7bddbc6840b812a4b57..38797675867280015f5d21662ed73808c0d50940 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1195,8 +1195,7 @@
"Zurück"
"Eingabemethode wechseln"
"Auswahl für die Eingabemethode öffnen"
-
-
+ "Einstellungen"
"Der Speicherplatz wird knapp"
"Einige Systemfunktionen funktionieren eventuell nicht."
"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."
@@ -1747,7 +1746,8 @@
"Die Berechtigungsanfrage wird durch eine andere App verdeckt. Daher kann deine Antwort nicht geprüft werden."
"Zum Auswählen der gewünschten Funktion tippen:"
"Funktionen auswählen, die du mit der Schaltfläche \"Bedienungshilfen\" verwenden möchtest"
- "Funktionen für Verknüpfung mit Lautstärketaste auswählen"
+
+
"%s wurde deaktiviert"
"Kurzbefehle bearbeiten"
"Fertig"
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index b9f3d54c501e80fd25a2a8d6d80da35ab5f60081..28a5e55fc284acf4fdffc8bf9952bc7c6b072679 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1029,7 +1029,7 @@
"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα %1$d φορές. \n\nΠροσπαθήστε ξανά σε %2$d δευτερόλεπτα."
"Έχετε πληκτρολογήσει τον αριθμό σας PIN εσφαλμένα %1$d φορές. \n\nΠροσπαθήστε ξανά σε %2$d δευτερόλεπτα."
"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα %1$d φορές. Μετά από %2$d ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε %3$d δευτερόλεπτα."
- "Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος %1$d φορές. Μετά από ακόμα %2$d ανεπιτυχείς προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε τη συσκευή σας Android TV χρησιμοποιώντας τα στοιχεία σύνδεσής σας στο Google.\n\nΠροσπαθήστε ξανά σε %3$d δευτερόλεπτα."
+ "Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος %1$d φορές. Μετά από ακόμα %2$d ανεπιτυχείς προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε τη συσκευή σας Android TV χρησιμοποιώντας τα στοιχεία σύνδεσής σας στην Google.\n\nΠροσπαθήστε ξανά σε %3$d δευτερόλεπτα."
"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα %1$d φορές. Μετά από %2$d ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε %3$d δευτερόλεπτα."
"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet %1$d φορές. Μετά από %2$d προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."
"Δοκιμάσατε να ξεκλειδώσετε τη συσκευή Android TV %1$d φορές χωρίς επιτυχία. Μετά από ακόμα %2$d ανεπιτυχείς προσπάθειες, θα γίνει επαναφορά των προεπιλεγμένων εργοστασιακών ρυθμίσεων στη συσκευή σας Android TV και όλα τα δεδομένα χρήστη θα χαθούν."
@@ -1195,8 +1195,7 @@
"Πίσω"
"Εναλλαγή μεθόδου εισαγωγής"
"Άνοιγμα εργαλείου επιλογής μεθόδου εισαγωγής"
-
-
+ "Ρυθμίσεις"
"Ο αποθηκευτικός χώρος εξαντλείται"
"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"
"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."
@@ -1747,7 +1746,8 @@
"Μια εφαρμογή αποκρύπτει το αίτημα άδειας, με αποτέλεσμα να μην είναι δυνατή η επαλήθευση της απάντησής σας."
"Πατήστε μια λειτουργία για να ξεκινήσετε να τη χρησιμοποιείτε:"
"Επιλέξτε τις λειτουργίες που θέλετε να χρησιμοποιείτε με το κουμπί προσβασιμότητας."
- "Επιλέξτε τις λειτουργίες που θέλετε να χρησιμοποιείτε με τη συντόμευση κουμπιού έντασης ήχου"
+
+
"Η υπηρεσία %s έχει απενεργοποιηθεί."
"Επεξεργασία συντομεύσεων"
"Τέλος"
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 6f265dfcd1a4ab25a06ae176dcb4e6497ca30f97..70d86e7de732891382ff19190a91994e096e845f 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1195,8 +1195,7 @@
"Back"
"Switch input method"
"Open input method picker"
-
-
+ "Settings"
"Storage space running out"
"Some system functions may not work"
"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."
@@ -1747,7 +1746,8 @@
"An app is obscuring the permission request so your response cannot be verified."
"Tap a feature to start using it:"
"Choose features to use with the Accessibility button"
- "Choose features to use with the volume key shortcut"
+
+
"%s has been turned off"
"Edit shortcuts"
"Done"
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 6a4bc1a4b47f98813f6c88cff41ed20529f81991..19547f81d6361953792b2c5aa705f207f704e376 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1195,8 +1195,7 @@
"Back"
"Switch input method"
"Open input method picker"
-
-
+ "Settings"
"Storage space running out"
"Some system functions may not work"
"Not enough storage for the system. Make sure you have 250MB of free space and restart."
@@ -1747,7 +1746,8 @@
"An app is obscuring the permission request so your response cannot be verified."
"Tap a feature to start using it:"
"Choose features to use with the accessibility button"
- "Choose features to use with the volume key shortcut"
+
+
"%s has been turned off"
"Edit shortcuts"
"Done"
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 0bf754c1d1e4ed8422b035e0bf83012e7c0c3922..d3f0c6410e24900bce4ef52009e24b5625ac792c 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1195,8 +1195,7 @@
"Back"
"Switch input method"
"Open input method picker"
-
-
+ "Settings"
"Storage space running out"
"Some system functions may not work"
"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."
@@ -1747,7 +1746,8 @@
"An app is obscuring the permission request so your response cannot be verified."
"Tap a feature to start using it:"
"Choose features to use with the Accessibility button"
- "Choose features to use with the volume key shortcut"
+
+
"%s has been turned off"
"Edit shortcuts"
"Done"
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 04f5d5cf9a4b070072be992e7eef74dfe823d293..7c3be159a01fc15fa4fcb9aef7a193d9fa99e0a9 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1195,8 +1195,7 @@
"Back"
"Switch input method"
"Open input method picker"
-
-
+ "Settings"
"Storage space running out"
"Some system functions may not work"
"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."
@@ -1747,7 +1746,8 @@
"An app is obscuring the permission request so your response cannot be verified."
"Tap a feature to start using it:"
"Choose features to use with the Accessibility button"
- "Choose features to use with the volume key shortcut"
+
+
"%s has been turned off"
"Edit shortcuts"
"Done"
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 74d2fbeb8825d5a9798e3c7c781643ea959b5e76..c1eade14fbc6a3ef274c5734f427cfce209c12a3 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1195,8 +1195,7 @@
"Back"
"Switch input method"
"Open input method picker"
-
-
+ "Settings"
"Storage space running out"
"Some system functions may not work"
"Not enough storage for the system. Make sure you have 250MB of free space and restart."
@@ -1747,7 +1746,8 @@
"An app is obscuring the permission request so your response cannot be verified."
"Tap a feature to start using it:"
"Choose features to use with the accessibility button"
- "Choose features to use with the volume key shortcut"
+
+
"%s has been turned off"
"Edit shortcuts"
"Done"
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b94201a27b4e95b6e4c879ddb25de695f800bb19..773edd3e5ba7a38d90d810fb61e03d29b752b3bf 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1196,8 +1196,7 @@
"Atrás"
"Cambiar método de entrada"
"Abrir selector de método de entrada"
-
-
+ "Configuración"
"Queda poco espacio de almacenamiento"
"Es posible que algunas funciones del sistema no estén disponibles."
"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."
@@ -1748,7 +1747,8 @@
"Una app está cubriendo la solicitud de permiso, por lo que no se puede verificar tu respuesta."
"Presiona una función para comenzar a usarla:"
"Selecciona las funciones a utilizar con el botón de accesibilidad"
- "Selecciona las funciones a usar con las teclas de volumen"
+
+
"Se desactivó %s"
"Editar accesos directos"
"Listo"
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 77f10f52d93eb3dc4943b88940311dfce1ed701f..ff7d912255cb9538e95621b8916c0519967f9cc5 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1196,8 +1196,7 @@
"Atrás"
"Cambiar método de introducción de texto"
"Abrir selector de método de introducción"
-
-
+ "Ajustes"
"Queda poco espacio"
"Es posible que algunas funciones del sistema no funcionen."
"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."
@@ -1748,7 +1747,8 @@
"Una aplicación está ocultando la solicitud de permiso, por lo que no se puede verificar tu respuesta."
"Toca una función para empezar a usarla:"
"Selecciona qué funciones usar con el botón de accesibilidad"
- "Selecciona qué funciones usar con la tecla de volumen"
+
+
"Se ha desactivado %s"
"Editar accesos directos"
"Hecho"
@@ -2393,13 +2393,13 @@
"No se puede proyectar a la pantalla"
"Usa otro cable y vuelve a intentarlo"
"Tu dispositivo está demasiado caliente y no puede proyectar a la pantalla hasta que se enfríe"
- "Dual Screen"
- "La función Dual Screen está activada"
+ "Pantalla dual"
+ "La función Pantalla dual está activada"
"%1$s está usando ambas pantallas para mostrar contenido"
"El dispositivo está demasiado caliente"
- "Dual Screen no está disponible porque el teléfono se está calentando demasiado"
- "Dual Screen no está disponible"
- "Dual Screen no está disponible porque la función Ahorro de batería está activada. Puedes desactivarla en Ajustes."
+ "Pantalla dual no está disponible porque el teléfono se está calentando demasiado"
+ "Pantalla dual no está disponible"
+ "Pantalla dual no está disponible porque la función Ahorro de batería está activada. Puedes desactivarla en Ajustes."
"Ir a Ajustes"
"Desactivar"
"%s configurado"
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 1db2c5d02eeaccbacadcb2a62dd8fd4b796e99cb..f313fb29b92a200c94d915a3fef7efcba4aceac8 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1195,8 +1195,7 @@
"Tagasi"
"Sisestusmeetodi vahetamine"
"Sisestusmeetodi valija avamine"
-
-
+ "Seaded"
"Talletusruum saab täis"
"Mõned süsteemifunktsioonid ei pruugi töötada"
"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."
@@ -1747,7 +1746,8 @@
"Rakendus varjab loataotlust, nii et teie vastust ei saa kinnitada."
"Puudutage funktsiooni, et selle kasutamist alustada."
"Valige funktsioonid, mida juurdepääsetavuse nupuga kasutada"
- "Valige helitugevuse nupu otsetee funktsioonid"
+
+
"%s on välja lülitatud"
"Muuda otseteid"
"Valmis"
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 6f927453613ca4808e76995555d666aecfdbedcf..b0775d0796a9239d4f78277966702eb57c4948f2 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -252,7 +252,7 @@
"Itzali egin nahi duzu?"
"Berrabiarazi modu seguruan"
"Modu seguruan berrabiarazi nahi duzu? Instalatutako hirugarrenen aplikazioak desgaituko dira. Berriro berrabiarazi ondoren leheneratuko dira."
- "Azkenak"
+ "Azkenaldikoak"
"Ez dago azkenaldian erabilitako aplikaziorik."
"Tabletaren aukerak"
"Android TV gailuaren aukerak"
@@ -1195,8 +1195,7 @@
"Atzera"
"Aldatu idazketa-metodoa"
"Ireki idazketa-metodoaren hautatzailea"
-
-
+ "Ezarpenak"
"Memoria betetzen ari da"
"Sistemaren funtzio batzuek ez dute agian funtzionatuko"
"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."
@@ -1335,7 +1334,7 @@
"Alarma-soinuak"
"Jakinarazpen-soinuak"
"Ezezaguna"
- "Hasi saioa Wi-Fi sarean"
+ "Hasi saioa wifi-sarean"
"Hasi saioa sarean"
@@ -1747,7 +1746,8 @@
"Aplikazio bat baimen-eskaera oztopatzen ari da eta, ondorioz, ezin da egiaztatu erantzuna."
"Eginbide bat erabiltzen hasteko, saka ezazu:"
"Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoiarekin"
- "Aukeratu zein eginbide erabili nahi duzun bolumen-botoien lasterbidearekin"
+
+
"Desaktibatu da %s"
"Editatu lasterbideak"
"Eginda"
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 728d87d43fbf93324df9efecc882f27e4c1827b1..5e474cde8a0930cebc847d97fb1dc6e7a498347b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1183,7 +1183,7 @@
"کپی URL"
"انتخاب متن"
"لغو"
- "بازانجام"
+ "ازنو انجام دادن"
"تکمیل خودکار"
"انتخاب متن"
"افزودن به واژهنامه"
@@ -1195,8 +1195,7 @@
"برگشت"
"تغییر روش ورودی"
"باز کردن انتخابگر روش ورودی"
-
-
+ "تنظیمات"
"فضای ذخیرهسازی درحال پر شدن است"
"برخی از عملکردهای سیستم ممکن است کار نکنند"
"فضای ذخیرهسازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راهاندازی مجدد کنید."
@@ -1747,7 +1746,8 @@
"پاسخ شما تأیید نشد زیرا یک برنامه درخواست اجازه را مسدود کرده است."
"برای استفاده از ویژگی، روی آن تکضرب بزنید:"
"انتخاب ویژگیهای موردنظر برای استفاده با دکمه دسترسپذیری"
- "انتخاب ویژگیهای موردنظر برای استفاده با میانبر کلید میزان صدا"
+
+
"%s خاموش شده است"
"ویرایش میانبرها"
"تمام"
@@ -2392,13 +2392,13 @@
"قرینهسازی روی نمایشگر ممکن نبود"
"از کابل دیگری استفاده کنید و دوباره امتحان کنید"
"دستگاه بسیار گرم است و تا زمانیکه خنک نشود نمیتواند محتوا را روی نمایشگر قرینهسازی کند."
- "Dual screen"
- "Dual Screen روشن است"
+ "صفحهنمایش دوگانه"
+ "«صفحهنمایش دوگانه» روشن است"
"%1$s از هر دو نمایشگر برای نمایش محتوا استفاده میکند"
"دستگاه بیشازحد گرم شده است"
- "Dual Screen دردسترس نیست زیرا تلفن بیشازحد گرم شده است"
- "Dual Screen دردسترس نیست"
- "Dual Screen دردسترس نیست چون «بهینهسازی باتری» روشن است. میتوانید این ویژگی را در «تنظیمات» خاموش کنید."
+ "«صفحهنمایش دوگانه» دردسترس نیست زیرا تلفن بیشازحد گرم شده است"
+ "«صفحهنمایش دوگانه» دردسترس نیست"
+ "«صفحهنمایش دوگانه» دردسترس نیست چون «بهینهسازی باتری» روشن است. میتوانید این ویژگی را در «تنظیمات» خاموش کنید."
"رفتن به تنظیمات"
"خاموش کردن"
"%s پیکربندی شد"
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 776b2dbcc80419df9f48b29a9f80c8520b1c0be0..dec5b64b9592588413e47acfe8169f2bc4ca5f23 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1195,8 +1195,7 @@
"Takaisin"
"Vaihda syöttötapaa"
"Avaa syöttötavan valinta"
-
-
+ "Asetukset"
"Tallennustila loppumassa"
"Kaikki järjestelmätoiminnot eivät välttämättä toimi"
"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."
@@ -1747,7 +1746,8 @@
"Sovellus peittää lupapyynnön, joten vastaustasi ei voi vahvistaa."
"Aloita ominaisuuden käyttö napauttamalla sitä:"
"Valitse ominaisuudet, joita käytetään esteettömyyspainikkeella"
- "Valitse ominaisuudet, joita käytetään äänenvoimakkuuspikanäppäimellä"
+
+
"%s on laitettu pois päältä"
"Muokkaa pikakuvakkeita"
"Valmis"
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 325615139f959c2f62cb905cde97ae5cfe2c7509..7cb9e06c287a142f3dd9af31cc33c45479db1f0b 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1196,8 +1196,7 @@
"Retour"
"Changer de méthode d\'entrée"
"Ouvrir le sélecteur de méthode d\'entrée"
-
-
+ "Paramètres"
"Espace de stockage bientôt saturé"
"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."
"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."
@@ -1748,7 +1747,8 @@
"Une appli masque la demande d\'autorisation de sorte que votre réponse ne peut pas être vérifiée."
"Toucher une fonctionnalité pour commencer à l\'utiliser :"
"Choisir les fonctionnalités à utiliser à l\'aide du bouton d\'accessibilité"
- "Choisir les fonctionnalités à utiliser avec le raccourci des touches de volume"
+
+
"%s a été désactivé"
"Modifier les raccourcis"
"OK"
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index a6aee4cb517bfc77a937f74d301b46a80b8d5af6..ca3998c6f43c61f650ca12ff5b245f29dc017780 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1196,8 +1196,7 @@
"Retour"
"Changer le mode de saisie"
"Ouvrir l\'outil de sélection du mode de saisie"
-
-
+ "Paramètres"
"Espace de stockage bientôt saturé"
"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."
"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."
@@ -1748,7 +1747,8 @@
"Une application masque la demande d\'autorisation. Votre réponse ne peut donc pas être vérifiée."
"Appuyez sur une fonctionnalité pour commencer à l\'utiliser :"
"Choisir les fonctionnalités à utiliser avec le bouton Accessibilité"
- "Choisir les fonctionnalités à utiliser avec le raccourci des touches de volume"
+
+
"Le service %s a été désactivé"
"Modifier les raccourcis"
"OK"
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 85d3eed2db66697543a963e82aa0292de5d0e55c..e07218cd955547e943cc434485ed63df3931fda9 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1195,8 +1195,7 @@
"Atrás"
"Cambia o método de introdución"
"Abrir o selector do método de introdución de texto"
-
-
+ "Configuración"
"Estase esgotando o espazo de almacenamento"
"É posible que algunhas funcións do sistema non funcionen"
"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."
@@ -1747,7 +1746,8 @@
"Hai unha aplicación que está ocultando a solicitude de permiso, polo que non se pode verificar a túa resposta."
"Tocar unha función para comezar a utilizala:"
"Escoller as funcións que queres utilizar co botón Accesibilidade"
- "Escolle as funcións que queres utilizar co atallo da tecla de volume"
+
+
"%s: desactivouse"
"Editar atallos"
"Feito"
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6c028edcd0e26297be3c8e222ffa2e4c456f0541..2810b5e40107210be0e3df6e763fb6d6b1e85f6a 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -90,7 +90,7 @@
"કટોકટી કૉલબૅક મોડ"
"મોબાઇલ ડેટાની સ્થિતિ"
"SMS મેસેજ"
- "વૉઇસમેઇલ સંદેશા"
+ "વૉઇસમેઇલ મેસેજ"
"વાઇ-ફાઇ કૉલિંગ"
"સિમનું સ્ટેટસ"
"સિમ કાર્ડનું ઉચ્ચ પ્રાધાન્યતાનું સ્ટેટસ"
@@ -122,7 +122,7 @@
"સેવા શોધી રહ્યું છે"
"વાઇ-ફાઇ કૉલિંગ સેટ કરી શકાયું નથી"
- - "વાઇ-ફાઇ પરથી કૉલ કરવા અને સંદેશા મોકલવા માટે પહેલાં તમારા કૅરિઅરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગમાંથી વાઇ-ફાઇ કૉલિંગ ફરીથી ચાલુ કરો. (ભૂલ કોડ: %1$s)"
+ - "વાઇ-ફાઇ પરથી કૉલ કરવા અને મેસેજ મોકલવા માટે પહેલાં તમારા મોબાઇલ ઑપરેટરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગમાંથી વાઇ-ફાઇ કૉલિંગ ફરીથી ચાલુ કરો. (ભૂલ કોડ: %1$s)"
- "તમારા કૅરિઅરમાં વાઇ-ફાઇ કૉલિંગ રજિસ્ટર કરવામાં સમસ્યા આવી: %1$s"
@@ -266,7 +266,7 @@
"સત્ર સમાપ્ત કરો"
"સ્ક્રીનશૉટ"
"બગ રિપોર્ટ"
- "આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."
+ "આ, એક ઇ-મેઇલ મેસેજ તરીકે મોકલવા માટે, તમારા વર્તમાન ડિવાઇસના સ્ટેટસ વિશે માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટના શરુ થવાથી લઈને મોકલવા માટે તૈયાર થવા સુધીની પ્રક્રિયામાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."
"ક્રિયાપ્રતિક્રિયાત્મક રિપોર્ટ"
"મોટાભાગના સંજોગોમાં આનો ઉપયોગ કરો. તે રિપોર્ટની પ્રગતિને ટ્રૅક કરવા, સમસ્યા વિશે વધુ વિગતો દાખલ કરવાની અને સ્ક્રીનશૉટ્સ લેવાની મંજૂરી આપે છે. તે કેટલાક ઓછા ઉપયોગમાં આવતાં વિભાગો કે જે જાણ કરવામાં વધુ સમય લેતાં હોય તેને છોડી દઈ શકે છે."
"પૂર્ણ રિપોર્ટ"
@@ -291,8 +291,8 @@
"સુરક્ષા"
"કાર મોડ"
"એકાઉન્ટ સ્થિતિ"
- "વિકાસકર્તા માટેના સંદેશા"
- "ડેવલપર માટેના મહત્ત્વપૂર્ણ સંદેશા"
+ "ડેવલપર માટેના મેસેજ"
+ "ડેવલપર માટેના મહત્ત્વપૂર્ણ મેસેજ"
"અપડેટ્સ"
"નેટવર્ક સ્થિતિ"
"નેટવર્ક ચેતવણીઓ"
@@ -324,7 +324,7 @@
"કૅલેન્ડર"
"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"
"SMS"
- "SMS સંદેશા મોકલવાની અને જોવાની"
+ "SMS મેસેજ મોકલો અને જુઓ"
"ફાઇલો"
"તમારા ડિવાઇસ પરની ફાઇલો ઍક્સેસ કરો"
"મ્યુઝિક અને ઑડિયો"
@@ -379,28 +379,28 @@
"એપ્લિકેશનને આઉટગોઇંગ કૉલ દરમિયાન કૉલને એક અલગ નંબર પર રીડાયરેક્ટ કરવા અથવા કૉલને સંપૂર્ણપણે છોડી દેવાનાં વિકલ્પ સાથે ડાયલ થઈ રહેલા નંબરને જોવાની મંજૂરી આપે છે."
"ફોન કૉલને જવાબ આપો"
"ઍપ્લિકેશનને ઇનકમિંગ ફોન કૉલને જવાબ આપવાની મંજૂરી આપે છે."
- "ટેક્સ્ટ સંદેશા (SMS) પ્રાપ્ત કરો"
- "ઍપ્લિકેશનને SMS સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ્લિકેશન તમને દર્શાવ્યા વિના તમારા ઉપકરણ પર મોકલેલ સંદેશાઓનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."
- "ટેક્સ્ટ સંદેશા (MMS) પ્રાપ્ત કરો"
- "ઍપ્લિકેશનને MMS સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ્લિકેશન તમને દર્શાવ્યા વિના તમારા ઉપકરણ પર મોકલેલ સંદેશાઓનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."
- "સેલ બ્રોડકાસ્ટ સંદેશા ફૉરવર્ડ કરો"
- "સેલ બ્રોડકાસ્ટ સંદેશા પ્રાપ્ત થાય કે તરત ફૉરવર્ડ કરવા માટે સેલ બ્રોડકાસ્ટ મૉડ્યૂલ સાથે પ્રતિબદ્ધ થવા બાબતે ઍપને મંજૂરી આપે છે. તમને કટોકટીની પરિસ્થિતિની ચેતવણી આપવા માટે સેલ બ્રોડકાસ્ટ અલર્ટ અમુક સ્થાનોમાં ડિલિવર કરવામાં આવે છે. કટોકટી અંગેનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય, ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઑપરેશનમાં વિક્ષેપ પાડે તેમ બની શકે છે."
+ "ટેક્સ્ટ મેસેજ (SMS) મેળવો"
+ "ઍપને SMS મેસેજ મેળવવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ તમને દર્શાવ્યા વિના તમારા ડિવાઇસ પર મોકલેલા મેસેજનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."
+ "ટેક્સ્ટ મેસેજ (MMS) મેળવો"
+ "ઍપને MMS મેસેજ પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ તમને દર્શાવ્યા વિના તમારા ડિવાઇસ પર મોકલેલા મેસેજને મૉનિટર કરી શકે છે અથવા ડિલીટ કરી શકે છે."
+ "સેલ બ્રોડકાસ્ટ મેસેજ ફૉરવર્ડ કરો"
+ "સેલ બ્રોડકાસ્ટ મેસેજ પ્રાપ્ત થાય કે તરત ફૉરવર્ડ કરવા માટે સેલ બ્રોડકાસ્ટ મૉડ્યૂલ સાથે પ્રતિબદ્ધ થવા બાબતે ઍપને મંજૂરી આપે છે. તમને કટોકટીની પરિસ્થિતિની ચેતવણી આપવા માટે સેલ બ્રોડકાસ્ટ અલર્ટ અમુક સ્થાનોમાં ડિલિવર કરવામાં આવે છે. કટોકટી અંગેનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય, ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઑપરેશનમાં વિક્ષેપ પાડે તેમ બની શકે છે."
"ચાલી રહેલા કૉલ મેનેજ કરો"
"ઍપને તમારા ડિવાઇસ પર ચાલુ કૉલ વિશેની વિગતો જોવાની અને આ કૉલને નિયંત્રિત કરવાની મંજૂરી આપે છે."
"છેલ્લી જ્ઞાત સેલ ઓળખને ઍક્સેસ કરો."
"ઍપને ટેલિફોન દ્વારા પ્રદાન કરવામાં આવેલી છેલ્લી જ્ઞાત સેલ ઓળખને ઍક્સેસ કરવાની મંજૂરી આપે છે."
- "સેલ બ્રોડકાસ્ટ સંદેશા વાંચો"
+ "સેલ બ્રોડકાસ્ટ મેસેજ વાંચો"
"ઍપ તમારા ડિવાઇસ દ્વારા પ્રાપ્ત થયેલ સેલ બ્રોડકાસ્ટ સંદેશાને વાંચવાની મંજૂરી આપે છે. સેલ બ્રોડકાસ્ટ ચેતવણીઓ તમને ઇમર્જન્સીની સ્થિતિઓ અંગે ચેતવવા માટે કેટલાક સ્થાનોમાં વિતરિત થાય છે. જ્યારે ઇમર્જન્સીનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઓપરેશનમાં હસ્તક્ષેપ કરી શકે છે."
"સબ્સ્ક્રાઇબ કરેલ ફીડ્સ વાંચો"
"એપ્લિકેશનને હાલમાં સમન્વયિત ફીડ્સ વિશે વિગતો મેળવવાની મંજૂરી આપે છે."
- "SMS સંદેશા મોકલો અને જુઓ"
- "એપ્લિકેશનને SMS સંદેશા મોકલવાની મંજૂરી આપે છે. આના પરિણામે અનપેક્ષિત શુલ્ક લાગી શકે છે. દુર્ભાવનાપૂર્ણ ઍપ્લિકેશનો તમારી પુષ્ટિ વિના સંદેશા મોકલીને તમારા નાણા ખર્ચાવી શકે છે."
- "તમારા ટેક્સ્ટ સંદેશા (SMS અથવા MMS) વાંચો"
- "આ ઍપ્લિકેશન, તમારા ટેબ્લેટ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."
- "આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."
- "આ ઍપ્લિકેશન, તમારા ફોન પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."
- "ટેક્સ્ટ સંદેશા (WAP) પ્રાપ્ત કરો"
- "એપ્લિકેશનને WAP સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આ પરવાનગીમાં તમને દર્શાવ્યા વિના તમને મોકલેલ સંદેશાઓનું નિરીક્ષણ કરવાની અને કાઢી નાખવાની ક્ષમતાનો સમાવેશ થાય છે."
+ "SMS મેસેજ મોકલો અને જુઓ"
+ "ઍપને SMS મેસેજ મોકલવાની મંજૂરી આપે છે. આના પરિણામે અનપેક્ષિત શુલ્ક લાગી શકે છે. દુર્ભાવનાપૂર્ણ ઍપ તમારા કન્ફર્મેશન વિના મેસેજ મોકલીને તમારા નાણા ખર્ચાવી શકે છે."
+ "તમારા ટેક્સ્ટ મેસેજ (SMS અથવા MMS) વાંચો"
+ "આ ઍપ, તમારા ટેબ્લેટ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."
+ "આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."
+ "આ ઍપ, તમારા ફોન પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."
+ "ટેક્સ્ટ મેસેજ (WAP) મેળવો"
+ "ઍપને WAP મેસેજ મેળવવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આ પરવાનગીમાં તમને દર્શાવ્યા વિના તમને મોકલેલા મેસેજનું નિરીક્ષણ કરવાની અને ડિલીટ કરવાની ક્ષમતાનો સમાવેશ થાય છે."
"ચાલુ ઍપ્લિકેશનો પુનઃપ્રાપ્ત કરો"
"એપ્લિકેશનને વર્તમાનમાં અને તાજેતરમાં ચાલી રહેલ Tasks વિશેની વિગતવાર માહિતી પુનઃપ્રાપ્ત કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને ઉપકરણ પર કઈ એપ્લિકેશન્સનો ઉપયોગ થાય છે તેના વિશેની માહિતી શોધવાની મંજૂરી આપી શકે છે."
"પ્રોફાઇલ અને ડિવાઇસ માલિકોને મેનેજ કરો"
@@ -492,9 +492,9 @@
"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત બધા કૅલેન્ડર ઇવેન્ટને વાંચી શકે છે અને તમારા કૅલેન્ડર ડેટાને શેર કરી અથવા સાચવી શકે છે."
"આ ઍપ્લિકેશન, તમારા ફોન પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સને વાંચી શકે છે અને તમારા કૅલેન્ડર ડેટાને શેર કરી અથવા સાચવી શકે છે."
"કૅલેન્ડર ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"
- "આ ઍપ્લિકેશન, તમારા ટેબ્લેટ પર કૅલેન્ડર ઇવેન્ટ્સ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ્લિકેશન, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ્સ બદલી શકે છે."
- "આ ઍપ, તમારા Android TV ડિવાઇસ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, કાઢી નાખી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."
- "આ ઍપ્લિકેશન, તમારા ફોન પર કૅલેન્ડર ઇવેન્ટ્સ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ્લિકેશન, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ્સ બદલી શકે છે."
+ "આ ઍપ, તમારા ટેબ્લેટ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના કૅલેન્ડર બદલી શકે છે."
+ "આ ઍપ, તમારા Android TV ડિવાઇસ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, કાઢી નાખી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."
+ "આ ઍપ, તમારા ફોન પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."
"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"
"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."
"ફૉરગ્રાઉન્ડમાં ફક્ત ચોક્કસ સ્થાન ઍક્સેસ કરો"
@@ -1101,7 +1101,7 @@
"એલાર્મ સેટ કરો"
"એપ્લિકેશનને ઇન્સ્ટોલ કરેલ અલાર્મ ઘડિયાળ એપ્લિકેશનમાં અલાર્મ સેટ કરવાની મંજૂરી આપે છે. કેટલીક અલાર્મ ઘડિયાળ ઍપ્લિકેશનો, આ સુવિધા લાગુ કરી શકતી નથી."
"વૉઇસમેઇલ ઉમેરો"
- "એપ્લિકેશનને તમારા વૉઇસમેઇલ ઇનબોક્સ પર સંદેશા ઉમેરવાની મંજૂરી આપે છે."
+ "એપને તમારા વૉઇસમેઇલ ઇનબોક્સ પર મેસેજ ઉમેરવાની મંજૂરી આપે છે."
"%1$s દ્વારા તમારા ક્લિપબોર્ડ પરથી પેસ્ટ કરવામાં આવ્યું"
"વધુ"
"મેનૂ+"
@@ -1195,8 +1195,7 @@
"પાછળ"
"ઇનપુટ પદ્ધતિ સ્વિચ કરો"
"ઇનપુટ પદ્ધતિ પિકર ખોલો"
-
-
+ "સેટિંગ"
"સ્ટોરેજ સ્થાન સમાપ્ત થયું"
"કેટલાક સિસ્ટમ Tasks કામ કરી શકશે નહીં"
"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."
@@ -1360,11 +1359,11 @@
"સ્વીકારો"
"નકારો"
"અક્ષર શામેલ કરો"
- "SMS સંદેશા મોકલી રહ્યું છે"
- "<b>%1$s</b> મોટા પ્રમાણમાં SMS સંદેશા મોકલી રહ્યું છે. શું તમે સંદેશા મોકલવાનું ચાલુ રાખવા માટે આ એપ્લિકેશનને મંજૂરી આપવા માગો છો?"
+ "SMS મેસેજ મોકલી રહ્યાં છે"
+ "<b>%1$s</b> મોટા પ્રમાણમાં SMS મેસેજ મોકલી રહ્યું છે. શું તમે મેસેજ મોકલવાનું ચાલુ રાખવા માટે આ એપને મંજૂરી આપવા માગો છો?"
"મંજૂરી આપો"
"નકારો"
- "<b>%1$s</b> તમને <b>%2$s</b> પર સંદેશ મોકલવા માગે છે."
+ "<b>%1$s</b>, <b>%2$s</b> પર મેસેજ મોકલવા માગે છે."
"આનાથી તમારા મોબાઇલ એકાઉન્ટ પર ""શુલ્ક લાગી શકે છે""."
"આનાથી તમારા મોબાઇલ એકાઉન્ટ પર શુલ્ક લાગશે."
"મોકલો"
@@ -1747,7 +1746,8 @@
"કોઈ ઍપ પરવાનગીની વિનંતીને ઢાંકી રહી છે, તેથી તમારા પ્રતિસાદની ચકાસણી કરી શકાતી નથી."
"સુવિધાનો ઉપયોગ શરૂ કરવા તેના પર ટૅપ કરો:"
"ઍક્સેસિબિલિટી બટન વડે તમે ઉપયોગમાં લેવા માગો છો તે સુવિધાઓ પસંદ કરો"
- "વૉલ્યૂમ કી શૉર્ટકટ વડે તમે ઉપયોગમાં લેવા માગો છો તે સુવિધાઓ પસંદ કરો"
+
+
"%s બંધ કરવામાં આવ્યું છે"
"શૉર્ટકટમાં ફેરફાર કરો"
"થઈ ગયું"
@@ -2037,7 +2037,7 @@
"Androidના કોઈ જૂના વર્ઝન માટે આ ઍપ બનાવવામાં આવી હતી. તે કદાચ યોગ્ય રીતે કામ કરતી નથી અને તેમાં નવીનતમ સુરક્ષા અને પ્રાઇવસી સંબંધિત સંરક્ષણો શામેલ નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."
"અપડેટ માટે તપાસો"
"આ ઍપ Androidના નવીનતમ વર્ઝન સાથે સુસંગત નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."
- "તમારી પાસે નવા સંદેશા છે"
+ "તમારી પાસે નવા મેસેજ છે"
"જોવા માટે SMS ઍપ્લિકેશન ખોલો"
"કેટલીક કાર્યક્ષમતા મર્યાદિત હોઈ શકે છે"
"કાર્યાલયની પ્રોફાઇલ લૉક કરી"
@@ -2153,7 +2153,7 @@
"ઓકે"
"બંધ કરો"
"વધુ જાણો"
- "Android 12માં Android માટે અનુકૂળ નોટિફિકેશનને બદલે વધુ સારા નોટિફિકેશન છે. આ સુવિધા સૂચિત ક્રિયાઓ અને જવાબો બતાવે છે તેમજ તમારા નોટિફિકેશનની યોગ્ય ગોઠવણી કરે છે.\n\nવધુ સારા નોટિફિકેશન સંપર્કોના નામ અને સંદેશા જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ ઍક્સેસ કરી શકે છે. આ સુવિધા ફોન કૉલના જવાબ આપવા કે \'ખલેલ પાડશો નહીં\'નું નિયંત્રણ કરવા જેવા નોટિફિકેશન છોડવાની કે તેનો જવાબ આપવાની ક્રિયા પણ કરી શકે છે."
+ "Android 12માં Android માટે અનુકૂળ નોટિફિકેશનને બદલે વધુ સારા નોટિફિકેશન છે. આ સુવિધા સૂચિત ક્રિયાઓ અને જવાબો બતાવે છે તેમજ તમારા નોટિફિકેશનની યોગ્ય ગોઠવણી કરે છે.\n\nવધુ સારા નોટિફિકેશન સંપર્કોના નામ અને મેસેજ જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ ઍક્સેસ કરી શકે છે. આ સુવિધા ફોન કૉલના જવાબ આપવા કે \'ખલેલ પાડશો નહીં\'નું નિયંત્રણ કરવા જેવા નોટિફિકેશન છોડવાની કે તેનો જવાબ આપવાની ક્રિયા પણ કરી શકે છે."
"રૂટિન મોડની માહિતીનું નોટિફિકેશન"
"બૅટરી સેવરની સુવિધા ચાલુ કરી છે"
"બૅટરીની આવરદા વધારવા માટે બૅટરીનો વપરાશ ઘટાડી રહ્યાં છીએ"
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 7bd182bb91dc3092e0b6b4f9a391c24dc1ba03e3..700e91ad8eb834486f8d8d0fa535db131271ebc6 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1195,8 +1195,7 @@
"वापस जाएं"
"इनपुट का तरीका बदलें"
"\'इनपुट का तरीका\' पिकर को खोलें"
-
-
+ "सेटिंग"
"मेमोरी में जगह नहीं बची है"
"हो सकता है कुछ सिस्टम फ़ंक्शन काम नहीं करें"
"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."
@@ -1747,7 +1746,8 @@
"ऐप्लिकेशन की वजह से, अनुमति का अनुरोध समझने में परेशानी हो रही है. इसलिए, आपके जवाब की पुष्टि नहीं की जा सकी."
"किसी सुविधा का इस्तेमाल करने के लिए, उस पर टैप करें:"
"सुलभता बटन पर टैप करके, इस्तेमाल करने के लिए सुविधाएं चुनें"
- "चुनें कि आवाज़ कम या ज़्यादा करने वाले बटन के शॉर्टकट से आप किन सुविधाओं का इस्तेमाल करना चाहते हैं"
+
+
"%s को बंद कर दिया गया है"
"शॉर्टकट में बदलाव करें"
"हो गया"
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e71e1511b2e6e40c84b42dc7adfd064bc7432d30..2596de21fadd135857c714f1002a1297e2d1aaa5 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1196,8 +1196,7 @@
"Natrag"
"Promjena načina unosa"
"Otvori alat za odabir načina unosa"
-
-
+ "Postavke"
"Ponestaje prostora za pohranu"
"Neke sistemske funkcije možda neće raditi"
"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."
@@ -1748,7 +1747,8 @@
"Aplikacija prekriva upit za dopuštenje pa se vaš odgovor ne može potvrditi."
"Dodirnite značajku da biste je počeli koristiti:"
"Odabir značajki za upotrebu pomoću gumba za Pristupačnost"
- "Odabir značajki za upotrebu pomoću prečaca tipki za glasnoću"
+
+
"Usluga %s je isključena"
"Uredite prečace"
"Gotovo"
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 01d78a2268ca3cbc015983bcb286e64e6cb0b63c..09af5060364302763635a4de8eaddda058fa73b0 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1195,8 +1195,7 @@
"Vissza"
"Beviteli módszer váltása"
"A bevitelimód-választó megnyitása"
-
-
+ "Beállítások"
"Kevés a szabad terület"
"Előfordulhat, hogy néhány rendszerfunkció nem működik."
"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."
@@ -1747,7 +1746,8 @@
"Az egyik alkalmazás eltakarja az engedélykérelmet, így az Ön válasza nem ellenőrizhető."
"Koppintson valamelyik funkcióra a használatához:"
"Kiválaszthatja a Kisegítő lehetőségek gombbal használni kívánt funkciókat"
- "Kiválaszthatja a hangerőgombbal használni kívánt funkciókat"
+
+
"%s kikapcsolva"
"Gyorsparancsszerkesztés"
"Kész"
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 9293a8384ac2567e31ae02793d469695f48bc859..a28f413b9572df35a76cf75128dc5590cc903aa8 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -525,7 +525,7 @@
"Այս հավելվածը կարող է հետզանգեր ստանալ՝ ցանկացած տեսախցիկի բացվելու (կնշվի բացող հավելվածը) և փակվելու դեպքում։"
"Թույլատրել հավելվածին կամ ծառայությանը օգտագործել որպես միջերեսի համակարգային օգտատեր։"
"Այս հավելվածին ձեր տեսախցիկը հասանելի է որպես առանց միջերեսի համակարգային օգտատեր։"
- "կառավարել թրթռումը"
+ "կառավարել թրթռոցը"
"Թույլ է տալիս հավելվածին կառավարել թրթռոցը:"
"Հավելվածին թույլ է տալիս օգտագործել սարքի թրթռալու ռեժիմը։"
"ուղղակիորեն զանգել հեռախոսահամարներին"
@@ -1195,8 +1195,7 @@
"Հետ"
"Փոխել ներածման եղանակը"
"Բացել ներածման եղանակի ընտրիչը"
-
-
+ "Կարգավորումներ"
"Հիշողությունը սպառվում է"
"Որոշ գործառույթներ կարող են չաշխատել"
"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"
@@ -1747,7 +1746,8 @@
"Հավելվածը թաքցնում է թույլտվության հայտը, ուստի ձեր պատասխանը հնարավոր չէ ստուգել։"
"Ընտրեք՝ որ գործառույթն օգտագործել"
"Ընտրեք գործառույթները, որոնք կբացվեն «Հատուկ գործառույթներ» կոճակի միջոցով"
- "Ընտրեք գործառույթները, որոնք կբացվեն ձայնի կարգավորման կոճակի միջոցով"
+
+
"%s ծառայությունն անջատված է"
"Փոփոխել դյուրանցումները"
"Պատրաստ է"
@@ -2424,7 +2424,7 @@
"Անվտանգության նկատառումներով՝ բովանդակությունը թաքցվել է ցուցադրումից"
"Ավտոմատ միացել է արբանյակին"
- "Դուք կարող եք ուղարկել և ստանալ հաղորդագրություններ՝ առանց բջջային կամ Wi-Fi կապի"
+ "Դուք կարող եք հաղորդագրություններ ուղարկել և ստանալ առանց բջջային կամ Wi-Fi կապի"
"Օգտագործե՞լ արբանյակային հաղորդագրումը"
"Ուղարկեք և ստացեք հաղորդագրություններ առանց բջջային կամ Wi-Fi ցանցի"
"Բացել Messages-ը"
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 3b1e037a0a474673b321e485307989a347b506fa..76c89eaa2ddf4f1903fe0103af202810661b0c17 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1195,8 +1195,7 @@
"Kembali"
"Beralih metode input"
"Buka pemilih metode input"
-
-
+ "Setelan"
"Ruang penyimpanan hampir habis"
"Beberapa fungsi sistem mungkin tidak dapat bekerja"
"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."
@@ -1747,7 +1746,8 @@
"Aplikasi menghalangi permintaan izin sehingga respons Anda tidak dapat diverifikasi."
"Ketuk fitur untuk mulai menggunakannya:"
"Pilih fitur yang akan digunakan dengan tombol aksesibilitas"
- "Pilih fitur yang akan digunakan dengan pintasan tombol volume"
+
+
"%s telah dinonaktifkan"
"Edit pintasan"
"Selesai"
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 7bc4ddffee25a32f0a1f2664d18dd94bd04ca35d..3eb2e4bd0a2c6daffcac16d3172d4d1f278aca70 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1195,8 +1195,7 @@
"Til baka"
"Skipta um innfærsluaðferð"
"Opna val á innfærsluaðferð"
-
-
+ "Stillingar"
"Geymslurýmið er senn á þrotum"
"Sumir kerfiseiginleikar kunna að vera óvirkir"
"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."
@@ -1747,7 +1746,8 @@
"Forrit er að fela heimildarbeiðnina svo ekki er hægt að staðfesta svarið þitt."
"Ýttu á eiginleika til að byrja að nota hann:"
"Veldu eiginleika sem á að nota með aðgengishnappinum"
- "Veldu eiginleika sem á að nota með flýtileið hljóðstyrkstakka"
+
+
"Slökkt hefur verið á %s"
"Breyta flýtileiðum"
"Lokið"
diff --git a/core/res/res/values-it-feminine/strings.xml b/core/res/res/values-it-feminine/strings.xml
index 141b46768311e87bb7aed341f1263b53ebb6bd9c..1a69a63153b537536a1096911874b4cb4b49ca57 100644
--- a/core/res/res/values-it-feminine/strings.xml
+++ b/core/res/res/values-it-feminine/strings.xml
@@ -20,6 +20,5 @@
- "Amica"
"Moglie"
diff --git a/core/res/res/values-it-masculine/strings.xml b/core/res/res/values-it-masculine/strings.xml
index 7310eb868f41cd75f110d5bee44ef9cdecac4279..86c4408ffe6469fa065f7d400007680ed2ffff9a 100644
--- a/core/res/res/values-it-masculine/strings.xml
+++ b/core/res/res/values-it-masculine/strings.xml
@@ -20,6 +20,5 @@
- "Amico"
"Marito"
diff --git a/core/res/res/values-it-neuter/strings.xml b/core/res/res/values-it-neuter/strings.xml
index ce433d728d23639e6fd925ebd50bcc1514b25b70..7c33e91b7dcc8487a006cb9c23beac298f882614 100644
--- a/core/res/res/values-it-neuter/strings.xml
+++ b/core/res/res/values-it-neuter/strings.xml
@@ -20,6 +20,5 @@
- "Amicə"
"Coniuge"
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index cb89354413b337ac0adad72dab83d809eaf95b92..29533d50eac8be2d202068ce972469610ff2ac62 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -969,7 +969,7 @@
"Figlio"
"Convivente"
"Padre"
- "Persona amica"
+ "Amico"
"Dirigente"
"Madre"
"Genitore"
@@ -1196,8 +1196,7 @@
"Indietro"
"Cambia metodo di immissione"
"Apri selettore metodo di immissione"
-
-
+ "Impostazioni"
"Spazio di archiviazione in esaurimento"
"Alcune funzioni di sistema potrebbero non funzionare"
"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."
@@ -1748,7 +1747,8 @@
"Un\'app nasconde la tua richiesta di autorizzazione, per cui non abbiamo potuto verificare la tua risposta."
"Tocca una funzionalità per iniziare a usarla:"
"Scegli le funzionalità da usare con il pulsante Accessibilità"
- "Scegli le funzionalità da usare con la scorciatoia dei tasti del volume"
+
+
"Il servizio %s è stato disattivato"
"Modifica scorciatoie"
"Fine"
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 41829df0e2ba94881a80cc6c6827fe6df62d4421..52b6173745b51577b351d22a1ecc833779f48c15 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1196,8 +1196,7 @@
"חזרה"
"החלפה של שיטת הקלט"
"פתיחה של בוחר שיטות הקלט"
-
-
+ "הגדרות"
"מקום האחסון עומד להיגמר"
"ייתכן שפונקציות מערכת מסוימות לא יפעלו"
"אין מספיק מקום אחסון עבור המערכת. עליך לוודא שיש לך מקום פנוי בנפח של 250MB ולהתחיל שוב."
@@ -1748,7 +1747,8 @@
"אפליקציה מסתירה את בקשת ההרשאה כך שלא ניתן לאמת את התשובה שלך."
"יש להקיש על תכונה כדי להתחיל להשתמש בה:"
"בחירת תכונה לשימוש עם לחצן הנגישות"
- "בחירת תכונות לשימוש עם מקש הקיצור לעוצמת הקול"
+
+
"שירות %s כבוי"
"עריכת קיצורי הדרך"
"סיום"
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 437a5e6f1342be3099958f2f3086fa8855ff677c..bcff8138c10353759438c334acd3d894aed123b0 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1195,8 +1195,7 @@
"戻る"
"入力方法の切り替え"
"入力方法の選択ツールを開く"
-
-
+ "設定"
"空き容量わずか"
"一部のシステム機能が動作しない可能性があります"
"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"
@@ -1747,7 +1746,8 @@
"権限のリクエストを遮っているアプリがあるため、同意の回答を確認できません。"
"使用を開始する機能をタップ:"
"ユーザー補助機能ボタンで使用する機能の選択"
- "音量ボタンのショートカットで使用する機能の選択"
+
+
"%s はオフになっています"
"ショートカットの編集"
"完了"
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index bc0ab8afc675654504e7680cda37e55ac890a139..0adebb002c05862295c0fc9a79a748ce55036f5c 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1195,8 +1195,7 @@
"უკან"
"შეყვანის მეთოდის გადართვა"
"შეყვანის მეთოდის ამომრჩევის გახსნა"
-
-
+ "პარამეტრები"
"თავისუფალი ადგილი იწურება"
"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"
"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."
@@ -1747,7 +1746,8 @@
"აპი მალავს ნებართვის მოთხოვნას, ასე რომ, თქვენი პასუხი ვერ დადასტურდება."
"შეეხეთ ფუნქციას მისი გამოყენების დასაწყებად:"
"აირჩიეთ ფუნქციები, რომელთა გამოყენებაც გსურთ მარტივი წვდომის ღილაკით"
- "აირჩიეთ ფუნქციები, რომელთა გამოყენებაც გსურთ ხმის ღილაკის მალსახმობით"
+
+
"%s გამორთულია"
"მალსახმობების რედაქტირება"
"მზადაა"
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 1574a78144c9e834cdfeb6c0fbb4ec84b2b24029..d9ee240ef48a65edfb21583a03586a6dff928b17 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1195,8 +1195,7 @@
"Артқа"
"Енгізу әдісін ауыстыру"
"Енгізу әдісін таңдау құралын ашу"
-
-
+ "Параметрлер"
"Жадта орын азайып барады"
"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"
"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."
@@ -1747,7 +1746,8 @@
"Қолданба рұқсат сұрауын жасырып тұрғандықтан, жауабыңыз расталмайды."
"Функцияны пайдалана бастау үшін түртіңіз:"
"\"Арнайы мүмкіндіктер\" түймесімен қолданылатын функцияларды таңдаңыз"
- "Дыбыс деңгейі пернелері тіркесімімен қолданылатын функцияларды таңдаңыз"
+
+
"%s қызметі өшірулі."
"Жылдам пәрмендерді өзгерту"
"Дайын"
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 4374bb67db46186235f55ea002511c7c9365dc5c..f3dd775defef9324e1b7228445f63e012ef7e035 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1195,8 +1195,7 @@
"ថយក្រោយ"
"ប្ដូរវិធីសាស្ត្របញ្ចូល"
"បើកផ្ទាំងជ្រើសរើសវិធីបញ្ចូល"
-
-
+ "ការកំណត់"
"អស់ទំហំផ្ទុក"
"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"
"មិនមានទំហំផ្ទុកគ្រប់គ្រាន់សម្រាប់ប្រព័ន្ធ។ សូមប្រាកដថាអ្នកមានទំហំទំនេរ 250MB ហើយចាប់ផ្ដើមឡើងវិញ។"
@@ -1747,7 +1746,8 @@
"កម្មវិធីមួយកំពុងបិទបាំងសំណើសុំការអនុញ្ញាត ដូច្នេះមិនអាចផ្ទៀងផ្ទាត់ការឆ្លើយតបរបស់អ្នកបានទេ។"
"ចុចមុខងារណាមួយ ដើម្បចាប់ផ្ដើមប្រើ៖"
"ជ្រើសរើសមុខងារ ដើម្បីប្រើជាមួយប៊ូតុងភាពងាយស្រួល"
- "ជ្រើសរើសមុខងារ ដើម្បីប្រើជាមួយផ្លូវកាត់គ្រាប់ចុចកម្រិតសំឡេង"
+
+
"បានបិទ %s"
"កែផ្លូវកាត់"
"រួចរាល់"
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 00f22af456a091f7d510ddf98e851b6c29efb015..a772e32c12214707a314567af2bbfb3368c208df 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1195,8 +1195,7 @@
"ಹಿಂದಕ್ಕೆ"
"ಇನ್ಪುಟ್ ವಿಧಾನವನ್ನು ಬದಲಿಸಿ"
"ಇನ್ಪುಟ್ ವಿಧಾನದ ಪಿಕರ್ ಅನ್ನು ತೆರೆಯಿರಿ"
-
-
+ "ಸೆಟ್ಟಿಂಗ್ಗಳು"
"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"
"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"
"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."
@@ -1404,7 +1403,7 @@
"USB ಪರಿಕರವನ್ನು ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"
"ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ."
"ಸಂಪರ್ಕಗೊಂಡಿರುವ ಸಾಧನವನ್ನು ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ. ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."
- "ಅನ್ಲಾಗ್ ಆಡಿಯೋ ಪರಿಕರ ಪತ್ತೆಯಾಗಿದೆ"
+ "ಅನ್ಲಾಗ್ ಆಡಿಯೋ ಆ್ಯಕ್ಸೆಸರಿ ಪತ್ತೆಯಾಗಿದೆ"
"ಲಗತ್ತಿಸಲಾದ ಸಾಧನವು ಈ ಫೋನಿನೊಂದಿಗೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."
"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"
"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"
@@ -1747,7 +1746,8 @@
"ಆ್ಯಪ್ವೊಂದು ಅನುಮತಿ ವಿನಂತಿಯನ್ನು ಮರೆಮಾಚುತ್ತಿರುವ ಕಾರಣ ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."
"ವೈಶಿಷ್ಟ್ದ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ಅದನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ:"
"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಬಟನ್ ಜೊತೆಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"
- "ವಾಲ್ಯೂಮ್ ಕೀ ಶಾರ್ಟ್ಕಟ್ ಜೊತೆಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"
+
+
"%s ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ"
"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ"
"ಪೂರ್ಣಗೊಂಡಿದೆ"
@@ -2153,7 +2153,7 @@
"ಸರಿ"
"ಆಫ್ ಮಾಡಿ"
"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"
- "ವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್ಗಳು Android 12 ರಲ್ಲಿ Android ಅಡಾಪ್ಟಿವ್ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಬದಲಾಯಿಸಿವೆ. ಈ ವೈಶಿಷ್ಟ್ಯವು ಸೂಚಿಸಿದ ಕ್ರಿಯೆಗಳು ಮತ್ತು ಪ್ರತ್ಯುತ್ತರಗಳನ್ನು ತೋರಿಸುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಯೋಜಿಸುತ್ತದೆ.\n\nವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್ಗಳು ಸಂಪರ್ಕ ಹೆಸರುಗಳು ಮತ್ತು ಸಂದೇಶಗಳಂತಹ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆ ವಿಷಯವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ಈ ವೈಶಿಷ್ಟ್ಯವು ಫೋನ್ ಕರೆಗಳಿಗೆ ಉತ್ತರಿಸುವುದು ಮತ್ತು \'ಅಡಚಣೆ ಮಾಡಬೇಡಿ\' ಅನ್ನು ನಿಯಂತ್ರಿಸುವಂತಹ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ವಜಾಗೊಳಿಸಬಹುದು ಅಥವಾ ಪ್ರತಿಕ್ರಿಯಿಸಬಹುದು."
+ "ವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್ಗಳು Android 12 ರಲ್ಲಿ Android ಅಡಾಪ್ಟಿವ್ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಬದಲಾಯಿಸಿವೆ. ಈ ವೈಶಿಷ್ಟ್ಯವು ಸೂಚಿಸಿದ ಕ್ರಿಯೆಗಳು ಮತ್ತು ಪ್ರತ್ಯುತ್ತರಗಳನ್ನು ತೋರಿಸುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಆಯೋಜಿಸುತ್ತದೆ.\n\nವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್ಗಳು ಸಂಪರ್ಕ ಹೆಸರುಗಳು ಮತ್ತು ಸಂದೇಶಗಳಂತಹ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ನೋಟಿಫಿಕೇಶನ್ ವಿಷಯವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ಈ ವೈಶಿಷ್ಟ್ಯವು ಫೋನ್ ಕರೆಗಳಿಗೆ ಉತ್ತರಿಸುವುದು ಮತ್ತು \'ಅಡಚಣೆ ಮಾಡಬೇಡಿ\' ಅನ್ನು ನಿಯಂತ್ರಿಸುವಂತಹ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ವಜಾಗೊಳಿಸಬಹುದು ಅಥವಾ ಪ್ರತಿಕ್ರಿಯಿಸಬಹುದು."
"ದೈನಂದಿನ ಸ್ಥಿತಿಯ ಮಾಹಿತಿಯ ನೋಟಿಫಿಕೇಶನ್"
"ಬ್ಯಾಟರಿ ಸೇವರ್ ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ"
"ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯನ್ನು ವಿಸ್ತರಿಸಲು ಬ್ಯಾಟರಿ ಬಳಕೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಲಾಗುತ್ತಿದೆ"
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 3c38d5fb4957479a6843813a021dc7489f7835de..0e47e59c35f958407c813e17179d2533cd8d0a74 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1195,8 +1195,7 @@
"뒤로"
"입력 방법 전환"
"입력 방법 선택 도구 열기"
-
-
+ "설정"
"저장 공간이 부족함"
"일부 시스템 기능이 작동하지 않을 수 있습니다."
"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."
@@ -1747,7 +1746,8 @@
"앱에서 권한 요청을 가려서 응답을 확인할 수 없습니다."
"기능을 사용하려면 탭하세요"
"접근성 버튼으로 사용할 기능 선택"
- "볼륨 키 단축키로 사용할 기능 선택"
+
+
"%s이(가) 사용 중지됨"
"단축키 수정"
"완료"
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 1f29fa884e4c77514cc9bcf32c146ce945429788..4c2ee5f88d9965563a0ce2f91d185ce9a8e08335 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1195,8 +1195,7 @@
"Артка"
"Киргизүү ыкмасын өзгөртүү"
"Киргизүү ыкмасын тандоо"
-
-
+ "Параметрлер"
"Сактагычта орун калбай баратат"
"Айрым функциялар иштебеши мүмкүн"
"Системада сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."
@@ -1747,7 +1746,8 @@
"Колдонмо уруксат суроону жашырып койгондуктан, жообуңузду ырастоо мүмкүн эмес."
"Функцияны колдонуп баштоо үчүн аны таптап коюңуз:"
"Атайын мүмкүнчүлүктөр баскычы менен колдонгуңуз келген функцияларды тандаңыз"
- "Үндү катуулатуу/акырындатуу баскычтары менен кайсы функцияларды иштеткиңиз келет?"
+
+
"%s өчүрүлдү"
"Кыска жолдорду түзөтүү"
"Бүттү"
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 3ea5e53a9ab13a224d94310b78e3734642e703af..b62750311caa0c52bb42cbd0dd7838c237cc331c 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1195,8 +1195,7 @@
"ກັບຄືນ"
"ສະຫຼັບວິທີການປ້ອນຂໍ້ມູນ"
"ເປີດຕົວເລືອກວິທີການປ້ອນຂໍ້ມູນ"
-
-
+ "ການຕັ້ງຄ່າ"
"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"
"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"
"ບໍ່ມີບ່ອນເກັບຂໍ້ມູນພຽງພໍສຳລັບລະບົບ. ກວດສອບໃຫ້ແນ່ໃຈວ່າທ່ານມີພື້ນທີ່ຫວ່າງຢ່າງໜ້ອຍ 250MB ແລ້ວລອງໃໝ່."
@@ -1747,7 +1746,8 @@
"ແອັບໜຶ່ງກຳລັງປິດບັງຄຳຮ້ອງຂໍການອະນຸຍາດ ດັ່ງນັ້ນຈຶ່ງບໍ່ສາມາດຢັ້ງຢືນຄຳຕອບຂອງທ່ານໄດ້."
"ແຕະໃສ່ຄຸນສົມບັດໃດໜຶ່ງເພື່ອເລີ່ມການນຳໃຊ້ມັນ:"
"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບປຸ່ມການຊ່ວຍເຂົ້າເຖິງ"
- "ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບທາງລັດປຸ່ມລະດັບສຽງ"
+
+
"ປິດ %s ໄວ້ແລ້ວ"
"ແກ້ໄຂທາງລັດ"
"ແລ້ວໆ"
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 78159b436f7d989cff1c92a231db4f27b1560d06..8eace89c0a6b6fc9501ae0f0387e76242dd89654 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1197,8 +1197,7 @@
"Atgal"
"Perjungti įvesties metodą"
"Atidaryti įvesties metodo rinkiklį"
-
-
+ "Nustatymai"
"Mažėja laisvos saugyklos vietos"
"Kai kurios sistemos funkcijos gali neveikti"
"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."
@@ -1749,7 +1748,8 @@
"Programa užstoja leidimo užklausą, todėl negalima patvirtinti jūsų atsakymo."
"Norėdami naudoti funkciją, palieskite ją:"
"Funkcijų, kurioms bus naudojamas pritaikomumo mygtukas, pasirinkimas"
- "Funkcijų, kurioms bus naudojamas garsumo spartusis klavišas, pasirinkimas"
+
+
"Paslauga „%s“ išjungta"
"Redaguoti sparčiuosius klavišus"
"Atlikta"
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 84c69f12ab6e04f9e4818e5926ac57ac8d2f7ccd..01fb9da42198feac46007713e1329929e4ad1687 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1196,8 +1196,7 @@
"Atpakaļ"
"Pārslēgt ievades metodi"
"Atvērt ievades metodes atlasītāju"
-
-
+ "Iestatījumi"
"Paliek maz brīvas vietas"
"Dažas sistēmas funkcijas var nedarboties."
"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."
@@ -1748,7 +1747,8 @@
"Kāda lietotne padara atļaujas pieprasījumu nesaprotamu, tāpēc nevar verificēt jūsu atbildi."
"Pieskarieties funkcijai, lai sāktu to izmantot"
"Izvēlieties funkcijas, ko izmantot ar pieejamības pogu"
- "Izvēlieties funkcijas, ko izmantot ar skaļuma pogu saīsni"
+
+
"Pakalpojums %s ir izslēgts."
"Rediģēt īsinājumtaustiņus"
"Gatavs"
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index e3e97a8b85b669ef585ad927f911b3902a5577b5..dbc4b757c4a2ee126253b010e9edeab6ac2ccc3b 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1195,8 +1195,7 @@
"Назад"
"Префрлете го методот за внесување"
"Отворете го избирачот на метод за внесување"
-
-
+ "Поставки"
"Капацитетот е речиси полн"
"Некои системски функции може да не работат"
"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 MB и рестартирајте."
@@ -1747,7 +1746,8 @@
"Апликација го прикрива барањето за дозвола, па вашиот одговор не може да се потврди."
"Допрете на функција за да почнете да ја користите:"
"Изберете ги функциите што ќе ги користите со копчето за пристапност"
- "Изберете ги функциите што ќе ги користите со кратенката за копчето за јачина на звук"
+
+
"%s е исклучена"
"Изменете ги кратенките"
"Готово"
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index cc15d1e4d3f59dcc7e49cf6e1b0f6b411348c9b6..2b1c52ff0825cf766429339b0df017f595824373 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1195,8 +1195,7 @@
"മടങ്ങുക"
"ഇൻപുട്ട് രീതി മാറുക"
"ഇൻപുട്ട് രീതി പിക്കർ തുറക്കുക"
-
-
+ "ക്രമീകരണം"
"സംഭരണയിടം കഴിഞ്ഞു"
"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."
"സിസ്റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."
@@ -1747,7 +1746,8 @@
"ഒരു ആപ്പ്, അനുമതി അഭ്യർത്ഥന മറയ്ക്കുന്നതിനാൽ നിങ്ങളുടെ പ്രതികരണം പരിശോധിച്ചുറപ്പിക്കാനാകില്ല."
"ഉപയോഗിച്ച് തുടങ്ങാൻ ഫീച്ചർ ടാപ്പ് ചെയ്യുക:"
"ഉപയോഗസഹായി ബട്ടണിന്റെ സഹായത്തോടെ, ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"
- "വോളിയം കീ കുറുക്കുവഴിയിലൂടെ ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"
+
+
"%s ഓഫാക്കിയിരിക്കുന്നു"
"കുറുക്കുവഴികൾ തിരുത്തുക"
"പൂർത്തിയാക്കി"
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index a523fce8108f30e8b17758be3bbd6b2b8d0f3c9a..bf2f78c9c9dbadc20f18f5c290ea486fe59c25cc 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1195,8 +1195,7 @@
"Буцах"
"Оруулах аргыг сэлгэх"
"Оруулах арга сонгогчийг нээх"
-
-
+ "Тохиргоо"
"Сангийн хэмжээ дутагдаж байна"
"Зарим систем функц ажиллахгүй байна"
"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."
@@ -1747,7 +1746,8 @@
"Апп зөвшөөрлийн хүсэлтийг хааж байгаа тул таны хариултыг баталгаажуулах боломжгүй."
"Үүнийг ашиглаж эхлэхийн тулд онцлог дээр товшино уу:"
"Хандалтын товчлуурын тусламжтай ашиглах онцлогуудыг сонгоно уу"
- "Дууны түвшний түлхүүрийн товчлолын тусламжтай ашиглах онцлогуудыг сонгоно уу"
+
+
"%s-г унтраалаа"
"Товчлолуудыг засах"
"Болсон"
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 37378b4a34e6d10f5ad72c7bfa8c0b890a388f82..67c3b980d70072491985bcbc30e872e432ea7fad 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1195,8 +1195,7 @@
"मागे जा"
"इनपुट पद्धत स्विच करा"
"इनपुट पद्धत पिकर उघडा"
-
-
+ "सेटिंग्ज"
"संचयन स्थान संपत आहे"
"काही सिस्टम कार्ये कार्य करू शकत नाहीत"
"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."
@@ -1747,7 +1746,8 @@
"परवानगी मागणारी विनंती अॅपमुळे अस्पष्ट होत असल्याने, तुमच्या प्रतिसादाची पडताळणी केली जाऊ शकत नाही."
"वैशिष्ट्य वापरणे सुरू करण्यासाठी त्यावर टॅप करा:"
"अॅक्सेसिबिलिटी बटणासोबत वापरायची असलेली ॲप्स निवडा"
- "व्हॉल्यूम की शॉर्टकटसोबत वापरायची असलेली ॲप्स निवडा"
+
+
"%s बंद केले आहे"
"शॉर्टकट संपादित करा"
"पूर्ण झाले"
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 41a64a0b5c48478e4d3fc962f9f788d3bb8f6f0f..5eac79316474989b9bf4a633bdab8f9015e793e4 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1195,8 +1195,7 @@
"Kembali"
"Tukar kaedah masukan"
"Buka pemilih kaedah input"
-
-
+ "Tetapan"
"Ruang storan semakin berkurangan"
"Beberapa fungsi sistem mungkin tidak berfungsi"
"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."
@@ -1747,7 +1746,8 @@
"Apl menghalang permintaan kebenaran, maka jawapan anda tidak dapat disahkan."
"Ketik ciri untuk mula menggunakan ciri itu:"
"Pilih ciri untuk digunakan dengan butang kebolehaksesan"
- "Pilih ciri untuk digunakan dengan pintasan kekunci kelantangan"
+
+
"%s telah dimatikan"
"Edit pintasan"
"Selesai"
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 229f1a156007c896153a937b70fcfa0f465b254b..b09539c27bac3008f0a5c3d06b79374058a781d1 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1195,8 +1195,7 @@
"နောက်သို့"
"လက်ကွက်ပြောင်းရန်"
"လက်ကွက်ရွေးစနစ် ဖွင့်ရန်"
-
-
+ "ဆက်တင်များ"
"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"
"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"
"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"
@@ -1747,7 +1746,8 @@
"အက်ပ်တစ်ခုသည် ခွင့်ပြုချက်တောင်းဆိုမှုကို ပိတ်နေသဖြင့် သင့်တုံ့ပြန်မှုကို စိစစ်၍မရပါ။"
"ဝန်ဆောင်မှုကို စတင်အသုံးပြုရန် တို့ပါ−"
"အများသုံးနိုင်မှု ခလုတ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"
- "အသံခလုတ် ဖြတ်လမ်းလင့်ခ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"
+
+
"%s ကို ပိတ်ထားသည်"
"ဖြတ်လမ်းများကို တည်းဖြတ်ရန်"
"ပြီးပြီ"
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 5e39528ecccbf85a677a8a159abda4c8eff04d10..8feb6e91557740b7ff1f76281e612d4f028450be 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1195,8 +1195,7 @@
"Tilbake"
"Bytt inndatametode"
"Åpne valg av inndatametode"
-
-
+ "Innstillinger"
"Lite ledig lagringsplass"
"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"
"Det er ikke nok lagringsplass for systemet. Kontroller at du har 250 MB ledig plass, og start på nytt."
@@ -1747,7 +1746,8 @@
"En app dekker forespørselen om tillatelse, så svaret ditt kan ikke bekreftes."
"Trykk på en funksjon for å begynne å bruke den:"
"Velg funksjonene du vil bruke med Tilgjengelighet-knappen"
- "Velg funksjonene du vil bruke med volumtastsnarveien"
+
+
"%s er slått av"
"Endre snarveier"
"Ferdig"
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 056e2537f0361fb189537ef699a1f94c99ef0ae3..9f3530a3e15ddc13dfbc8fc97543d36a8518b555 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1097,7 +1097,7 @@
"यस पृष्ठलाई छोड्नुहोस्"
"यही पृष्ठमा रहनुहोस्"
"%s\n\nके तपाईं यो पेजबाट नेभिगेट गर्न चाहनु हुन्छ भन्ने निश्चत छ?"
- "%1$s मार्फत स्वतः भरण गर्नुहोस्"
+ "%1$s मार्फत अटोफिल गर्नुहोस्"
"एउटा आलर्म सेट गर्नुहोस्"
"स्थापना गरिएको सङ्केत घडी एपमा सङ्केत समय मिलाउन एपलाई अनुमति दिन्छ। केही सङ्केत घडी एपहरूले यो सुविधा कार्यान्वयन नगर्न सक्छन्।"
"भ्वाइसमेल थप गर्नुहोस्"
@@ -1184,7 +1184,7 @@
"पाठ चयन गर्नुहोस्"
"अन्डू गर्नुहोस्"
"रिडू गर्नुहोस्"
- "स्वतः भरण"
+ "अटोफिल"
"पाठ चयनता"
"शब्दकोशमा थप्नुहोस्"
"मेट्नुहोस्"
@@ -1195,8 +1195,7 @@
"पछाडि"
"इनपुट विधि बदल्नुहोस्"
"इनपुट विधि पिकर खोल्नुहोस्"
-
-
+ "सेटिङ"
"भण्डारण ठाउँ सकिँदै छ"
"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"
"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"
@@ -1443,7 +1442,7 @@
"%s अन्य एपहरूमा देखिँदैछ"
"%s अन्य एपहरूमा देखिँदैछ"
"तपाईं %s ले यो विशेषता प्रयोग नगरेको चाहनुहुन्न भने सेटिङहरू खोली यसलाई निष्क्रिय पार्न ट्याप गर्नुहोस्।"
- "निष्क्रिय पार्नुहोस्"
+ "अफ गर्नुहोस्"
"जाँच गर्दै %s…"
"हालको सामग्री समीक्षा गर्दै"
"मिडिया भण्डारणको जाँच गरिँदै छ"
@@ -1747,7 +1746,8 @@
"कुनै एपका कारण अनुमतिसम्बन्धी अनुरोध बुझ्न कठिनाइ भइरहेकाले तपाईंको जवाफको पुष्टि गर्न सकिएन।"
"कुनै सुविधा प्रयोग गर्न थाल्न उक्त सुविधामा ट्याप गर्नुहोस्:"
"पहुँचको बटनमार्फत प्रयोग गर्न चाहेका सुविधाहरू छनौट गर्नुहोस्"
- "भोल्युम कुञ्जीको सर्टकटमार्फत प्रयोग गर्न चाहेका सुविधाहरू छनौट गर्नुहोस्"
+
+
"%s निष्क्रिय पारिएको छ"
"सर्टकटहरू सम्पादन गर्नुहोस्"
"सम्पन्न भयो"
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index dc2675f7abf9046610f2c797fac0694bd5dde7e6..8209504a62f30a374f7e72fbec6e8cd1f1c8d19c 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1195,8 +1195,7 @@
"Terug"
"Invoermethode wijzigen"
"Kiezer voor invoermethoden openen"
-
-
+ "Instellingen"
"Opslagruimte is bijna vol"
"Bepaalde systeemfuncties werken mogelijk niet"
"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."
@@ -1722,7 +1721,7 @@
" — "
"Verwijderen"
"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."
- "Wil je blijven luisteren op hoog volume?\n\nHet hoofdtelefoonvolume is langer dan de aanbevolen tijd hoog geweest. Dit kan je gehoor beschadigen."
+ "Wil je blijven luisteren op hoog volume?\n\nHet koptelefoonvolume is langer dan de aanbevolen tijd hoog geweest. Dit kan je gehoor beschadigen."
"Hard geluid gedetecteerd\n\nHet hoofdtelefoonvolume is hoger dan aanbevolen. Dit kan je gehoor beschadigen."
"Snelkoppeling toegankelijkheid gebruiken?"
"Als de snelkoppeling aanstaat, houd je beide volumeknoppen 3 seconden ingedrukt om een toegankelijkheidsfunctie te starten."
@@ -1747,7 +1746,8 @@
"Een app dekt het verzoek om rechten af, waardoor je reactie niet kan worden geverifieerd."
"Tik op een functie om deze te gebruiken:"
"Functies kiezen voor gebruik met de knop Toegankelijkheid"
- "Functies kiezen voor gebruik met de sneltoets via de volumeknop"
+
+
"%s is uitgezet"
"Snelkoppelingen bewerken"
"Klaar"
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 0ddb32901c47e3573221c56527a8fc740457432c..45b6e9ac8e75eeb8ab33a058301b97cb0daec2c5 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1195,8 +1195,7 @@
"ପଛକୁ ଫେରନ୍ତୁ"
"ଇନପୁଟ ପଦ୍ଧତି ସ୍ୱିଚ କରନ୍ତୁ"
"ଇନପୁଟ ପଦ୍ଧତି ପିକରକୁ ଖୋଲନ୍ତୁ"
-
-
+ "ସେଟିଂସ"
"ଷ୍ଟୋରେଜ୍ ସ୍ପେସ୍ ଶେଷ ହେବାରେ ଲାଗିଛି"
"କିଛି ସିଷ୍ଟମ ପ୍ରକାର୍ଯ୍ୟ କାମ କରିନପାରେ"
"ସିଷ୍ଟମ୍ ପାଇଁ ପ୍ରର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍ ନାହିଁ। ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ଆପଣଙ୍କ ପାଖରେ 250MB ଖାଲି ଜାଗା ଅଛି ଏବଂ ପୁନଃ ଆରମ୍ଭ କରନ୍ତୁ।"
@@ -1747,7 +1746,8 @@
"ଏକ ଆପ ଅନୁମତି ଅନୁରୋଧକୁ ଅସ୍ପଷ୍ଟ କରୁଛି ତେଣୁ ଆପଣଙ୍କ ଉତ୍ତରକୁ ଯାଞ୍ଚ କରାଯାଇପାରିବ ନାହିଁ।"
"ଏକ ଫିଚର୍ ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ଏହାକୁ ଟାପ୍ କରନ୍ତୁ:"
"ଆକ୍ସେସିବିଲିଟୀ ବଟନ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"
- "ଭଲ୍ୟୁମ୍ କୀ ସର୍ଟକଟ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"
+
+
"%s ବନ୍ଦ ହୋଇଯାଇଛି"
"ସର୍ଟକଟଗୁଡ଼ିକୁ ଏଡିଟ କରନ୍ତୁ"
"ହୋଇଗଲା"
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index ea616f672dff78920131af698934b036bc08ed34..c6d0b9fa1d9ba9203c361f7d4c3276024b962bde 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1195,8 +1195,7 @@
"ਪਿੱਛੇ"
"ਇਨਪੁੱਟ ਵਿਧੀ ਨੂੰ ਸਵਿੱਚ ਕਰੋ"
"ਇਨਪੁੱਟ ਵਿਧੀ ਚੋਣਕਾਰ ਨੂੰ ਖੋਲ੍ਹੋ"
-
-
+ "ਸੈਟਿੰਗਾਂ"
"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"
"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"
"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"
@@ -1747,7 +1746,8 @@
"ਕੋਈ ਐਪ ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਬੇਨਤੀ ਨੂੰ ਅਸਪਸ਼ਟ ਕਰ ਰਹੀ ਹੈ, ਇਸ ਲਈ ਤੁਹਾਡੇ ਜਵਾਬ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"
"ਕਿਸੇ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਵਰਤਣਾ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਉਸ \'ਤੇ ਟੈਪ ਕਰੋ:"
"ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨਾਲ ਵਰਤਣ ਲਈ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚੁਣੋ"
- "ਅਵਾਜ਼ ਕੁੰਜੀ ਸ਼ਾਰਟਕੱਟ ਨਾਲ ਵਰਤਣ ਲਈ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚੁਣੋ"
+
+
"%s ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ"
"ਸ਼ਾਰਟਕੱਟਾਂ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"
"ਹੋ ਗਿਆ"
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b302a2c93d4e3120884b72eabbc6d2275c523c30..0ae3bbf5c496c8e6b23137e0ab3c0f03140522ea 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1197,8 +1197,7 @@
"Wstecz"
"Przełącz metodę wprowadzania"
"Otwórz selektor metody wprowadzania"
-
-
+ "Ustawienia"
"Kończy się miejsce"
"Niektóre funkcje systemu mogą nie działać"
"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."
@@ -1749,7 +1748,8 @@
"Aplikacja zasłania prośbę o uprawnienia, więc nie można zweryfikować Twojej odpowiedzi."
"Wybierz funkcję, aby zacząć z niej korzystać:"
"Wybierz funkcje, których chcesz używać z przyciskiem ułatwień dostępu"
- "Wybierz funkcje, do których chcesz używać skrótu z klawiszami głośności"
+
+
"Usługa %s została wyłączona"
"Edytuj skróty"
"OK"
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 334f8c9b32aee7b8c388a07e639e29bfe758734f..e025d19879d737adaeaba891fb838dac40976852 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1196,8 +1196,7 @@
"Voltar"
"Mudar o método de entrada"
"Abrir o seletor de método de entrada"
-
-
+ "Configurações"
"Pouco espaço de armazenamento"
"Algumas funções do sistema podem não funcionar"
"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."
@@ -1403,7 +1402,7 @@
"MIDI via USB ativado"
"Dispositivo conectado como Webcam"
"Acessório USB conectado"
- "Toque para conferir mais opções."
+ "Toque para mais opções."
"Carregando dispositivo conectado. Toque para ver mais opções."
"Acessório de áudio analógico detectado"
"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."
@@ -1748,7 +1747,8 @@
"Um app está ocultando a solicitação de permissão e impedindo a verificação da sua resposta."
"Toque em um recurso para começar a usá-lo:"
"Escolha recursos para usar com o botão de acessibilidade"
- "Escolha recursos para usar com o atalho da tecla de volume"
+
+
"O %s foi desativado"
"Editar atalhos"
"Concluído"
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 70fa9f97d15fcad3f10d91b93ce1b30108a81acf..71940ef78cbd26d5a0db5878f664a27c43234c47 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1196,8 +1196,7 @@
"Voltar"
"Alternar o método de introdução"
"Abrir o selecionador do método de introdução"
-
-
+ "Definições"
"Está quase sem espaço de armazenamento"
"Algumas funções do sistema poderão não funcionar"
"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."
@@ -1748,7 +1747,8 @@
"Uma app está a ocultar o pedido de autorização e, por isso, não é possível validar a sua resposta."
"Toque numa funcionalidade para começar a utilizá-la:"
"Escolha funcionalidades para utilizar com o botão Acessibilidade"
- "Escolha funcionalidades para usar com o atalho das teclas de volume"
+
+
"O serviço %s foi desativado."
"Editar atalhos"
"Concluído"
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 334f8c9b32aee7b8c388a07e639e29bfe758734f..e025d19879d737adaeaba891fb838dac40976852 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1196,8 +1196,7 @@
"Voltar"
"Mudar o método de entrada"
"Abrir o seletor de método de entrada"
-
-
+ "Configurações"
"Pouco espaço de armazenamento"
"Algumas funções do sistema podem não funcionar"
"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."
@@ -1403,7 +1402,7 @@
"MIDI via USB ativado"
"Dispositivo conectado como Webcam"
"Acessório USB conectado"
- "Toque para conferir mais opções."
+ "Toque para mais opções."
"Carregando dispositivo conectado. Toque para ver mais opções."
"Acessório de áudio analógico detectado"
"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."
@@ -1748,7 +1747,8 @@
"Um app está ocultando a solicitação de permissão e impedindo a verificação da sua resposta."
"Toque em um recurso para começar a usá-lo:"
"Escolha recursos para usar com o botão de acessibilidade"
- "Escolha recursos para usar com o atalho da tecla de volume"
+
+
"O %s foi desativado"
"Editar atalhos"
"Concluído"
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 12ad4f331fe8c5ff2a96ce69d5c6ba340c15613f..7e5e7c0d900aedaca6f56a5ef5505be93fcb99c1 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1196,8 +1196,7 @@
"Înapoi"
"Schimbă metoda de introducere"
"Deschide selectorul metodei de introducere a textului"
-
-
+ "Setări"
"Spațiul de stocare aproape ocupat"
"Este posibil ca unele funcții de sistem să nu funcționeze"
"Spațiu de stocare insuficient pentru sistem. Asigură-te că ai 250 MB de spațiu liber și repornește."
@@ -1748,7 +1747,8 @@
"O aplicație blochează solicitarea de permisiune, așa că răspunsul nu se poate verifica."
"Atinge o funcție ca să începi să o folosești:"
"Alege funcțiile pe care să le folosești cu butonul de accesibilitate"
- "Alege funcțiile pentru comanda rapidă a butonului de volum"
+
+
"%s a fost dezactivat"
"Editează comenzile rapide"
"Gata"
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0ae47781ba1dea9aecec18539596223c0b038517..22501f2a01b16576a1b95184e97e8f00f451ce31 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1197,8 +1197,7 @@
"Назад"
"Сменить способ ввода"
"Выбрать способ ввода"
-
-
+ "Настройки"
"Недостаточно памяти"
"Некоторые функции могут не работать"
"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."
@@ -1749,7 +1748,8 @@
"Невозможно принять ваш ответ, поскольку запрос разрешения скрыт другим приложением."
"Выберите, какую функцию использовать:"
"Выберите функции, которые будут запускаться с помощью кнопки специальных возможностей"
- "Выберите функции, которые будут запускаться с помощью кнопки регулировки громкости"
+
+
"Сервис \"%s\" отключен."
"Изменить ярлыки"
"Готово"
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 4544ccb245d6bebe5ff00441f8de351c700083c4..af835d92bf713ca70f4dc4acb05b20a082cef79a 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1195,8 +1195,7 @@
"ආපසු"
"ආදාන ක්රමය මාරු කිරීම"
"ආදාන ක්රම තෝරකය විවෘත කරන්න"
-
-
+ "සැකසීම්"
"ආචයනය ඉඩ ප්රමාණය අඩු වී ඇත"
"සමහර පද්ධති කාර්යයන් ක්රියා නොකරනු ඇත"
"පද්ධතිය සඳහා ප්රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."
@@ -1747,7 +1746,8 @@
"යෙදුමක් අවසර ඉල්ලීම අඳුරු කරන බැවින්, ඔබේ ප්රතිචාරය සත්යාපනය කළ නොහැක."
"එය භාවිත කිරීම ආරම්භ කිරීමට විශේෂාංගයක් තට්ටු කරන්න:"
"ප්රවේශ්යතා බොත්තම සමග භාවිත කිරීමට විශේෂාංග තෝරා ගන්න"
- "හඬ පරිමා යතුරු කෙටිමග සමග භාවිත කිරීමට විශේෂාංග තෝරා ගන්න"
+
+
"%s ක්රියාවිරහිත කර ඇත"
"කෙටිමං සංස්කරණ කරන්න"
"නිමයි"
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 359ca8ff69fef8eab9cb98dab2f485ccad1bff64..3289bbc7416ff08e1581c0849480d9c36597e0f6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1197,8 +1197,7 @@
"Späť"
"Prepnúť metódu vstupu"
"Otvoriť výber metódy vstupu"
-
-
+ "Nastavenia"
"Nedostatok ukladacieho priestoru"
"Niektoré systémové funkcie nemusia fungovať"
"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."
@@ -1749,7 +1748,8 @@
"Aplikácia zakrýva žiadosť o povolenie, takže vaša odpoveď sa nedá overiť."
"Klepnutím na funkciu ju začnite používať:"
"Výber funkcií, ktoré chcete používať tlačidlom dostupnosti"
- "Výber funkcií, ktoré chcete používať klávesovou skratkou"
+
+
"Služba %s bola vypnutá"
"Upraviť skratky"
"Hotovo"
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 146a4c250735a692459f3023a5eee98160afced1..99ce0469be55e1e4ca7dbb72508573affd76b6ac 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1197,8 +1197,7 @@
"Nazaj"
"Preklop načina vnosa"
"Odpiranje izbirnika načina vnosa"
-
-
+ "Nastavitve"
"Prostor za shranjevanje bo pošel"
"Nekatere sistemske funkcije morda ne delujejo"
"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."
@@ -1749,7 +1748,8 @@
"Aplikacija zakriva zahtevo za dovoljenje, zato ni mogoče potrditi vašega odgovora."
"Če želite začeti uporabljati funkcijo, se je dotaknite:"
"Izberite funkcije, ki jih želite uporabljati z gumbom za dostopnost"
- "Izberite funkcije, ki jih želite uporabljati z bližnjico na tipki za glasnost"
+
+
"Storitev %s je izklopljena"
"Uredi bližnjice"
"Končano"
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 896783aa19fa44b8d270869af16ab25c365ece22..d8fe10944e2e0997217996e9a36f489956bec9bd 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1195,8 +1195,7 @@
"Pas"
"Ndërro metodën e hyrjes"
"Hap zgjedhësin e metodës së hyrjes"
-
-
+ "Cilësimet"
"Hapësira ruajtëse po mbaron"
"Disa funksione të sistemit mund të mos punojnë"
"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."
@@ -1747,7 +1746,8 @@
"Një aplikacion po fsheh kërkesën për leje, prandaj përgjigja jote nuk mund të verifikohet."
"Trokit te një veçori për të filluar ta përdorësh atë:"
"Zgjidh veçoritë që do të përdorësh me butonin e qasshmërisë"
- "Zgjidh veçoritë që do të përdorësh me shkurtoren e tastit të volumit"
+
+
"%s është çaktivizuar"
"Redakto shkurtoret"
"U krye"
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4bcd8337426f564aebde6808331e376502071649..0451b561347bf713096058505a814d42bfeac3bd 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1196,8 +1196,7 @@
"Назад"
"Промените метод уноса"
"Отвори бирач метода уноса"
-
-
+ "Подешавања"
"Меморијски простор је на измаку"
"Неке системске функције можда не функционишу"
"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."
@@ -1748,7 +1747,8 @@
"Апликација крије захтев за дозволу, па одговор не може да се верификује."
"Додирните неку функцију да бисте почели да је користите:"
"Одаберите функције које ћете користити са дугметом Приступачност"
- "Одаберите функције за пречицу тастером јачине звука"
+
+
"Услуга %s је искључена"
"Измените пречице"
"Готово"
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index edd5929453ca2254decd6211387609fa179bcefe..9bde6afc80f857b6c69fb5a260f5147fc290eaf7 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1195,8 +1195,7 @@
"Tillbaka"
"Byt inmatningsmetod"
"Öppna inmatningsmetodsväljaren"
-
-
+ "Inställningar"
"Lagringsutrymmet börjar ta slut"
"Det kan hända att vissa systemfunktioner inte fungerar"
"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."
@@ -1747,7 +1746,8 @@
"En app döljer behörighetsbegäran så det går inte att verifiera svaret."
"Tryck på funktioner som du vill aktivera:"
"Välj vilka funktioner du vill använda med hjälp av tillgänglighetsknappen"
- "Välj att funktioner att använda med hjälp av volymknappskortkommandot"
+
+
"%s har inaktiverats"
"Redigera genvägar"
"Klar"
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 0c10e9c9059eb93ce00b6b7b422575c3a7ebe077..c286eeb48d2ce843bc272d20003fcd96427917e0 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1195,8 +1195,7 @@
"Rudi nyuma"
"Badilisha mbinu ya kuingiza data"
"Fungua kiteua mbinu ya kuingiza data"
-
-
+ "Mipangilio"
"Nafasi ya kuhifadhi inakaribia kujaa"
"Baadhi ya vipengee vya mfumo huenda visifanye kazi"
"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."
@@ -1747,7 +1746,8 @@
"Programu inazuia ombi la ruhusa kwa hivyo jibu lako haliwezi kuthibitishwa."
"Gusa kipengele ili uanze kukitumia:"
"Chagua vipengele vya kutumia na kitufe cha zana za ufikivu"
- "Chagua vipengele vya kutumia na njia ya mkato ya kitufe cha sauti"
+
+
"Huduma ya %s imezimwa"
"Kubadilisha njia za mkato"
"Nimemaliza"
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 7bbe00a946247f679da71ce125580ea18d673598..b1ce5149861d8119520d2ab7e09b76ec8994be2d 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1195,8 +1195,7 @@
"பின்செல்லும்"
"உள்ளீட்டு முறையை மாற்றும்"
"உள்ளீட்டு முறைத் தேர்வுக் கருவியைத் திறக்கும்"
-
-
+ "அமைப்புகள்"
"சேமிப்பிடம் குறைகிறது"
"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"
"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."
@@ -1747,7 +1746,8 @@
"அணுகல் கோரிக்கையை ஓர் ஆப்ஸ் மறைப்பதால் உங்கள் பதிலைச் சரிபார்க்க முடியாது."
"ஒரு அம்சத்தைப் பயன்படுத்த அதைத் தட்டவும்:"
"அணுகல்தன்மை பட்டன் மூலம் பயன்படுத்த விரும்பும் அம்சங்களைத் தேர்வுசெய்யுங்கள்"
- "ஒலியளவு விசை ஷார்ட்கட் மூலம் பயன்படுத்த விரும்பும் அம்சங்களைத் தேர்வுசெய்யுங்கள்"
+
+
"%s ஆஃப் செய்யப்பட்டுள்ளது"
"ஷார்ட்கட்களை மாற்று"
"முடிந்தது"
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 003d7e9a4fee76437a3f5e9f157e33792f6bd41f..4b655d156a5059d56ef0bc3c3df0a2121a3ebb67 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1195,8 +1195,7 @@
"వెనుకకు"
"ఇన్పుట్ విధానాన్ని మార్చండి"
"ఇన్పుట్ విధాన సెలెక్టర్ను తెరవండి"
-
-
+ "సెట్టింగ్లు"
"స్టోరేజ్ ఖాళీ అయిపోతోంది"
"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"
"సిస్టమ్ కోసం తగినంత స్టోరేజ్ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."
@@ -1747,7 +1746,8 @@
"ఒక యాప్ అనుమతి రిక్వెస్ట్కు అడ్డు తగులుతోంది కాబట్టి మీ సమాధానం వెరిఫై చేయడం సాధ్యం కాదు."
"ఫీచర్ని ఉపయోగించడం ప్రారంభించడానికి, దాన్ని ట్యాప్ చేయండి:"
"యాక్సెసిబిలిటీ బటన్తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"
- "వాల్యూమ్ కీ షార్ట్కట్తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"
+
+
"%s ఆఫ్ చేయబడింది"
"షార్ట్కట్లను ఎడిట్ చేయండి"
"పూర్తయింది"
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 19bd98bff43fe5f0f3b3423872595bcfc53e1b27..d979c340e2168d68c6c20d04b5c38f784e3b39b6 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1195,8 +1195,7 @@
"กลับ"
"สลับวิธีการป้อนข้อมูล"
"เปิดเครื่องมือเลือกวิธีการป้อนข้อมูล"
-
-
+ "การตั้งค่า"
"พื้นที่จัดเก็บเหลือน้อย"
"บางฟังก์ชันระบบอาจไม่ทำงาน"
"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"
@@ -1747,7 +1746,8 @@
"มีแอปหนึ่งบดบังคำขอสิทธิ์ เราจึงยืนยันการตอบกลับของคุณไม่ได้"
"แตะฟีเจอร์เพื่อเริ่มใช้"
"เลือกฟีเจอร์ที่จะใช้กับปุ่มการช่วยเหลือพิเศษ"
- "เลือกฟีเจอร์ที่จะใช้กับทางลัดปุ่มปรับระดับเสียง"
+
+
"ปิด %s แล้ว"
"แก้ไขทางลัด"
"เสร็จ"
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index a53ca7836234ba4aaac91d8b3c6b53f22c63c527..fbbb32c1c8fa56186055779394a9ca1d98d61672 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1195,8 +1195,7 @@
"Bumalik"
"Magpalit ng pamamaraan ng pag-input"
"Buksan ang picker ng pamamaraan ng pag-input"
-
-
+ "Mga Setting"
"Nauubusan na ang puwang ng storage"
"Maaaring hindi gumana nang tama ang ilang paggana ng system"
"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."
@@ -1722,7 +1721,7 @@
" — "
"Alisin"
"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."
- "Magpatuloy sa pakikinig nang may malakas na volume?\n\nNaging malakas ang volume nang mas matagal na sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"
+ "Magpatuloy sa pakikinig nang may malakas na volume?\n\nMalakas ang volume nang mas matagal na sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"
"Naka-detect ng malakas na tunog\n\nMas malakas ang volume kaysa sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"
"Gagamitin ang Shortcut sa Accessibility?"
"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume."
@@ -1747,7 +1746,8 @@
"May app na pumipigil sa kahilingan sa pahintulot kaya hindi ma-verify ang iyong sagot."
"I-tap ang isang feature para simulan itong gamitin:"
"Pumili ng mga feature na gagana sa pamamagitan ng button ng accessibility"
- "Pumili ng mga feature na gagana sa pamamagitan ng shortcut ng volume key"
+
+
"Na-off ang %s"
"I-edit ang mga shortcut"
"Tapos na"
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 4c603e03809dee681f13434261b65cc13ee81920..a015a8f6cca3c3dffc4e0314561695a33269082c 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1195,8 +1195,7 @@
"Geri"
"Giriş yöntemini değiştir"
"Giriş yöntemi seçiciyi aç"
-
-
+ "Ayarlar"
"Depolama alanı bitiyor"
"Bazı sistem işlevleri çalışmayabilir"
"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."
@@ -1747,7 +1746,8 @@
"Bir uygulama, izin isteğini gizlediğinden yanıtınız doğrulanamıyor."
"Kullanmaya başlamak için bir özelliğe dokunun:"
"Erişilebilirlik düğmesiyle kullanılacak özellikleri seçin"
- "Ses tuşu kısayoluyla kullanılacak özellikleri seçin"
+
+
"%s kapatıldı"
"Kısayolları düzenle"
"Bitti"
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 1153830b6159c5527bbcee5793c01abe26b56d93..e41d3ce1b925258324122c23522c23d39b78326d 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1197,8 +1197,7 @@
"Назад"
"Змінити метод введення"
"Відкрити засіб вибору методу введення"
-
-
+ "Налаштування"
"Закінчується пам’ять"
"Деякі системні функції можуть не працювати"
"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."
@@ -1749,7 +1748,8 @@
"Інший додаток перекриває запит на доступ, тому вашу відповідь не вдається підтвердити."
"Натисніть функцію, щоб почати використовувати її:"
"Виберіть функції для кнопки спеціальних можливостей"
- "Виберіть функції для комбінації з клавішами гучності"
+
+
"Сервіс %s вимкнено"
"Змінити"
"Готово"
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 55a917d41a43dde4ea6438bf1b6c28b12e7cba60..d2f0898acd0c64e9c395ee556cbcda0dbef51a84 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1195,8 +1195,7 @@
"پیچھے"
"اندراج کا طریقہ سوئچ کریں"
"اندراج کے طریقے کا منتخب کنندہ کھولیں"
-
-
+ "ترتیبات"
"اسٹوریج کی جگہ ختم ہو رہی ہے"
"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"
"سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"
@@ -1747,7 +1746,8 @@
"ایپ اجازت کی درخواست کو مبہم کر رہی ہے لہذا آپ کے جواب کی تصدیق نہیں کی جا سکتی۔"
"ایک خصوصیت کا استعمال شروع کرنے کیلئے اسے تھپتھپائیں:"
"ایکسیسبیلٹی بٹن کے ساتھ استعمال کرنے کیلئے خصوصیات منتخب کریں"
- "والیوم کلید کے شارٹ کٹ کے ساتھ استعمال کرنے کیلئے خصوصیات منتخب کریں"
+
+
"%s کو آف کر دیا گیا ہے"
"شارٹ کٹس میں ترمیم کریں"
"ہو گیا"
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index f2451d54aff737bc9497597c1e8ee10eb9c7effc..e9bc805e303a2f0e2b81a170b5d51d5c70466991 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1195,8 +1195,7 @@
"Orqaga"
"Matn kiritish usulini almashtirish"
"Kiritish usulini tanlash oynasini ochish"
-
-
+ "Sozlamalar"
"Xotirada joy yetarli emas"
"Ayrim funksiyalar ishlamasligi mumkin"
"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."
@@ -1747,7 +1746,8 @@
"Ilova ruxsat olish talabini berkitmoqda, shu sababdan javobingizni tasdiqlash imkonsiz."
"Kerakli funksiyani tanlang"
"Qulayliklar tugmasi bilan foydalanish uchun funksiyalarni tanlang"
- "Tovush tugmasi bilan ishga tushiriladigan funksiyalarni tanlang"
+
+
"%s faolsizlantirildi"
"Tezkor tugmalarni tahrirlash"
"OK"
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 7c354a4584ca1e23e12ba819d035a312ce37eee0..510edab26fb427aa4266dc7e9c53a28640446dda 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1195,8 +1195,7 @@
"Quay lại"
"Chuyển phương thức nhập"
"Mở bộ chọn phương thức nhập"
-
-
+ "Cài đặt"
"Sắp hết dung lượng lưu trữ"
"Một số chức năng hệ thống có thể không hoạt động"
"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."
@@ -1747,7 +1746,8 @@
"Một ứng dụng đang che khuất yêu cầu quyền này nên chúng tôi không thể xác minh phản hồi của bạn."
"Nhấn vào một tính năng để bắt đầu sử dụng:"
"Chọn các tính năng để dùng với nút hỗ trợ tiếp cận"
- "Chọn các tính năng để dùng với phím tắt là phím âm lượng"
+
+
"%s đã bị tắt"
"Chỉnh sửa phím tắt"
"Xong"
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 1b8dd3a08d9991f623807e1b467fe163b9a5be02..bcb54dcac5780427a08e25483126813a6cf14431 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1195,8 +1195,7 @@
"返回"
"切换输入法"
"打开输入法选择器"
-
-
+ "设置"
"存储空间不足"
"某些系统功能可能无法正常使用"
"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"
@@ -1747,7 +1746,8 @@
"应用遮挡了权限请求,因此我们无法验证您的回复。"
"点按相应功能即可开始使用:"
"选择可通过“无障碍”按钮使用的功能"
- "选择可通过音量键快捷方式使用的功能"
+
+
"已关闭%s"
"修改快捷方式"
"完成"
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 91ccc079cad5fb02d6cfdb831f26986c7c06f647..875eec021f7bfdb0355e3fd1f9a7fd82d1ec8481 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1195,8 +1195,7 @@
"返回"
"切換輸入方法"
"打開輸入方法點選器"
-
-
+ "設定"
"儲存空間即將用盡"
"部分系統功能可能無法運作"
"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"
@@ -1747,7 +1746,8 @@
"有應用程式阻擋權限要求,因此系統無法驗證你的回應。"
"輕按即可開始使用所需功能:"
"選擇要配搭無障礙功能按鈕使用的功能"
- "選擇要用音量快速鍵的功能"
+
+
"%s 已關閉"
"編輯捷徑"
"完成"
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8f5ae1943c2217ce310ce173942c71d19fce0ccb..efa4c519ee195d06373dfc4b9a681d95f146dd31 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1195,8 +1195,7 @@
"返回"
"切換輸入法"
"開啟輸入法挑選器"
-
-
+ "設定"
"儲存空間即將用盡"
"部分系統功能可能無法運作"
"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"
@@ -1747,7 +1746,8 @@
"應用程式遮擋了權限要求,因此系統無法驗證你的回覆。"
"輕觸即可開始使用所需功能:"
"選擇要搭配無障礙工具按鈕使用的功能"
- "選擇要搭配音量快速鍵使用的功能"
+
+
"「%s」已關閉"
"編輯捷徑"
"完成"
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f09e9224b0dab6e7a3746b64c5804935ca40efe9..67a43611b2f3e5981d01bcfe9b4d5bb64be35d48 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1195,8 +1195,7 @@
"Emuva"
"Shintsha indlela yokufaka"
"Vula okokukhetha kwendlela yokufaka"
-
-
+ "Amasethingi"
"Isikhala sokulondoloza siyaphela"
"Eminye imisebenzi yohlelo ingahle ingasebenzi"
"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."
@@ -1747,7 +1746,8 @@
"I-app ifihla isicelo semvume ngakho impendulo yakho ayikwazi ukuqinisekiswa."
"Thepha isici ukuqala ukusisebenzisa:"
"Khetha izici ongazisebenzisa nenkinobho yokufinyeleleka"
- "Khetha izici ongazisebenzisa nesinqamuleli sokhiye wevolumu"
+
+
"I-%s ivaliwe"
"Hlela izinqamuleli"
"Kwenziwe"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b6468ee7a12c4822e2d6a0e7b8ed53d72c321af4..38aff7590a42135772ab05a8f3a7ce504633f34d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4153,6 +4153,9 @@
false
+
+ false
+
false
@@ -7030,6 +7033,10 @@
{@link InputDevice#SOURCE_ROTARY_ENCODER}s. -->
false
+
+ false
+
true
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index a7240ffc6012603acc93ae47e8c1f333ef770184..69437b44fd6ebeb7136e239efe2ab0abd38b707f 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -440,10 +440,6 @@
-
-
-
-
10
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f404666107242d4f8a5fd060369691db58634d22..606c7fdaa95471b411251b74bf7de5145eb5312a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4804,9 +4804,9 @@
| | |
+ from volume keys shortcut. [CHAR LIMIT=100] -->
Choose features to use with the
- volume key shortcut
+ volume keys shortcut
@@ -1535,7 +1535,7 @@ please see styles_device_defaults.xml.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cf0fc6159f428295bfa7fba80a98373f9c6bebb1..46938948b1335da1e68c93a34dfeec75a57f4aff 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2132,6 +2132,7 @@
+
@@ -5490,8 +5491,11 @@
+
+
+
@@ -5566,6 +5570,7 @@
+
@@ -5575,6 +5580,7 @@
+
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 452ae04c71998b3e733027d9954563a69502c838..d35bfb7bc1a1518109763ce52de1207cede716e9 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -131,7 +131,7 @@ easier.
- @style/Widget.DeviceDefault.ProgressBar.Small.Inverse
- @style/Widget.DeviceDefault.ProgressBar.Large.Inverse
- @dimen/config_progressBarCornerRadius
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @style/Widget.DeviceDefault.SeekBar
- @style/Widget.DeviceDefault.RatingBar
- @style/Widget.DeviceDefault.RatingBar.Indicator
@@ -351,7 +351,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -468,7 +468,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -587,7 +587,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -705,7 +705,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -831,7 +831,7 @@ easier.
- @style/Theme.DeviceDefault.Dialog.Alert
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -948,7 +948,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -1064,7 +1064,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -1181,7 +1181,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -1314,7 +1314,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -1432,7 +1432,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -1548,7 +1548,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -1666,7 +1666,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -1783,7 +1783,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -1900,7 +1900,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -2017,7 +2017,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -2134,7 +2134,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -2251,7 +2251,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -2373,7 +2373,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -2488,7 +2488,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -2642,7 +2642,7 @@ easier.
- @style/Widget.DeviceDefault.Light.ProgressBar.Small.Inverse
- @style/Widget.DeviceDefault.Light.ProgressBar.Large.Inverse
- @dimen/config_progressBarCornerRadius
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @style/Widget.DeviceDefault.Light.SeekBar
- @style/Widget.DeviceDefault.Light.RatingBar
- @style/Widget.DeviceDefault.Light.RatingBar.Indicator
@@ -2858,7 +2858,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -2974,7 +2974,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -3091,7 +3091,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -3210,7 +3210,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -3328,7 +3328,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -3452,7 +3452,7 @@ easier.
- @color/foreground_device_default_dark
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -3572,7 +3572,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -3691,7 +3691,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -3811,7 +3811,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -4133,7 +4133,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -4254,7 +4254,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -4373,7 +4373,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -4491,7 +4491,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -4608,7 +4608,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -4725,7 +4725,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -4840,7 +4840,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -5066,7 +5066,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -5162,7 +5162,7 @@ easier.
- @style/Theme.DeviceDefault.Light.Dialog.Alert
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -5280,7 +5280,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -5471,7 +5471,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -5522,7 +5522,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
@@ -5641,7 +5641,7 @@ easier.
- @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog
- - @color/config_progress_background_tint
+ - ?attr/materialColorOutline
- @dimen/config_progressBarCornerRadius
diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp
index 0e3bc6562edbd042fd24b681437c8c6bf92aee5c..a252f8b53fabf09e254cf1f5a581747cf81c4674 100644
--- a/core/tests/GameManagerTests/Android.bp
+++ b/core/tests/GameManagerTests/Android.bp
@@ -37,3 +37,10 @@ android_test {
certificate: "platform",
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "FrameworksCoreGameManagerTests_android_app",
+ base: "FrameworksCoreGameManagerTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.app"],
+}
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index 94bde68fdf119c97010e25d811127c68741682bd..127dbfd25b092ddd7ba8e562f42a5689fca03a02 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -20,6 +20,7 @@
+
@@ -31,7 +32,8 @@
+ android:exported="true"
+ android:enabled="false">
@@ -41,5 +43,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c23c1484cf59d389b43c1c3af9198baa42b57e1a
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml
@@ -0,0 +1,20 @@
+
+
+
+ Launch Battery Stats Viewer
+ The Battery Stats Viewer will be visible in the Launcher after it is opened once.
+
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..b01648838695b0ea79c05fa08d37a5acb07dd1e8
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.core.batterystatsviewer;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+public class TrampolineActivity extends Activity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ showLauncherIcon();
+ launchMainActivity();
+ }
+
+ private void showLauncherIcon() {
+ PackageManager pm = getPackageManager();
+ pm.setComponentEnabledSetting(new ComponentName(this, BatteryConsumerPickerActivity.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
+ }
+
+ private void launchMainActivity() {
+ startActivity(new Intent(this, BatteryConsumerPickerActivity.class));
+ }
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index edf461a4d5a67abbbf881e97fc241c4102dcdfaa..99cbf0504be67861798c5db4db3ef6a74fc66ea3 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -21,6 +21,7 @@ filegroup {
srcs: [
"DisabledTestApp/src/**/*.java",
"EnabledTestApp/src/**/*.java",
+ "BinderFrozenStateChangeCallbackTestApp/src/**/*.java",
"BinderProxyCountingTestApp/src/**/*.java",
"BinderProxyCountingTestService/src/**/*.java",
"BinderDeathRecipientHelperApp/src/**/*.java",
@@ -138,6 +139,7 @@ android_test {
":BinderDeathRecipientHelperApp1",
":BinderDeathRecipientHelperApp2",
":com.android.cts.helpers.aosp",
+ ":BinderFrozenStateChangeCallbackTestApp",
":BinderProxyCountingTestApp",
":BinderProxyCountingTestService",
":AppThatUsesAppOps",
@@ -803,3 +805,11 @@ test_module_config {
include_annotations: ["android.platform.test.annotations.PlatinumTest"],
exclude_annotations: FLAKY_OR_IGNORED,
}
+
+test_module_config {
+ name: "FrameworksCoreTests_android_tracing",
+ base: "FrameworksCoreTests",
+ team: "trendy_team_windowing_tools",
+ test_suites: ["device-tests"],
+ include_filters: ["android.tracing"],
+}
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 99b73a4afb93a72c9817f191df38bca2531d1baf..b1f1e2c2db05280eb47ee36876707481fcae5f3f 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -22,6 +22,7 @@
+
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp
new file mode 100644
index 0000000000000000000000000000000000000000..de97ddae6b18ca5ddeb64f7a51769c71d7743496
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // 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: "BinderFrozenStateChangeCallbackTestApp",
+
+ static_libs: ["coretests-aidl"],
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..29c8f5587f3a82c2bbf785558e348390936abf00
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
new file mode 100644
index 0000000000000000000000000000000000000000..77e8a404a0ffd0cdee64a4286bf8bfcbc0ecdbd6
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.coretests.bfscctestapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.frameworks.coretests.aidl.IBfsccTestAppCmdService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class BfsccTestAppCmdService extends Service {
+ private IBfsccTestAppCmdService.Stub mBinder = new IBfsccTestAppCmdService.Stub() {
+ private final LinkedBlockingQueue mNotifications =
+ new LinkedBlockingQueue<>();
+
+ @Override
+ public void listenTo(IBinder binder) throws RemoteException {
+ binder.addFrozenStateChangeCallback(
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state)
+ -> mNotifications.offer(state));
+ }
+
+ @Override
+ public boolean[] waitAndConsumeNotifications() {
+ List results = new ArrayList<>();
+ try {
+ IBinder.IFrozenStateChangeCallback.State state =
+ mNotifications.poll(5, TimeUnit.SECONDS);
+ if (state != null) {
+ results.add(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ }
+ } catch (InterruptedException e) {
+ return null;
+ }
+ while (mNotifications.size() > 0) {
+ results.add(mNotifications.poll()
+ == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ }
+ boolean[] convertedResults = new boolean[results.size()];
+ for (int i = 0; i < results.size(); i++) {
+ convertedResults[i] = results.get(i).booleanValue();
+ }
+ return convertedResults;
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
index 41b4c69232f42e7cdf44eb18dbfcf7b6be08e07b..09d79a69476ca702fcb2a30d665e9938e08c7aee 100644
--- a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
@@ -50,4 +50,4 @@ public class BinderProxyCountingService extends Service {
public IBinder onBind(Intent intent) {
return mBinder;
}
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
new file mode 100644
index 0000000000000000000000000000000000000000..d8d7dc4b72dbc48ac0e14704cbd1afef48916172
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.coretests.aidl;
+
+interface IBfsccTestAppCmdService {
+ void listenTo(IBinder binder);
+ boolean[] waitAndConsumeNotifications();
+}
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index c08e42b7179c78eee03d2e1f638b74cc65b9dd99..e47ef2df48b93afbf471eec2d9fba5910d48474b 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -232,6 +232,33 @@ public class NotificationChannelTest {
fromParcel.getSound().toString().length());
}
+ @Test
+ @EnableFlags({Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API,
+ Flags.FLAG_NOTIF_CHANNEL_CROP_VIBRATION_EFFECTS})
+ public void testLongVibrationFields_canWriteToXml() throws Exception {
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ // populate pattern with contents
+ long[] pattern = new long[65550 / 2];
+ for (int i = 0; i < pattern.length; i++) {
+ pattern[i] = 100;
+ }
+ channel.setVibrationPattern(pattern); // with flag on, also sets effect
+
+ // Send it through parceling & unparceling to simulate being passed through a binder call
+ NotificationChannel fromParcel = writeToAndReadFromParcel(channel);
+ assertThat(fromParcel.getVibrationPattern().length).isEqualTo(
+ NotificationChannel.MAX_VIBRATION_LENGTH);
+
+ // Confirm that this also survives writing to & restoring from XML
+ NotificationChannel result = backUpAndRestore(fromParcel);
+ assertThat(result.getVibrationPattern().length).isEqualTo(
+ NotificationChannel.MAX_VIBRATION_LENGTH);
+ assertThat(result.getVibrationEffect()).isNotNull();
+ assertThat(result.getVibrationEffect()
+ .computeCreateWaveformOffOnTimingsOrNull())
+ .isEqualTo(result.getVibrationPattern());
+ }
+
@Test
public void testRestoreSoundUri_customLookup() throws Exception {
Uri uriToBeRestoredUncanonicalized = Uri.parse("content://media/1");
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 10aed8d51d0904f74c43865ca257410ed0649506..14292725506e4e2c2f426d5d3a775c5548451b68 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -16,8 +16,6 @@
package android.graphics;
-import static com.android.text.flags.Flags.FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -32,7 +30,6 @@ import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.graphics.text.PositionedGlyphs;
import android.graphics.text.TextRunShaper;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.FontConfig;
@@ -931,7 +928,6 @@ public class TypefaceSystemFallbackTest {
return String.format(xml, op, lang, font);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_prepend() {
final ArrayMap fontMap = new ArrayMap<>();
@@ -947,7 +943,6 @@ public class TypefaceSystemFallbackTest {
assertB3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_replace() {
final ArrayMap fontMap = new ArrayMap<>();
@@ -963,7 +958,6 @@ public class TypefaceSystemFallbackTest {
assertB3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_append() {
final ArrayMap fontMap = new ArrayMap<>();
@@ -979,7 +973,6 @@ public class TypefaceSystemFallbackTest {
assertA3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_ScriptMismatch() {
final ArrayMap fontMap = new ArrayMap<>();
@@ -995,7 +988,6 @@ public class TypefaceSystemFallbackTest {
assertA3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_SubscriptMatch() {
final ArrayMap fontMap = new ArrayMap<>();
diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee2e7e06081e6efe675d5ae0d45079f99a542694
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.frameworks.coretests.aidl.IBfsccTestAppCmdService;
+import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests functionality of {@link android.os.IBinder.IFrozenStateChangeCallback}.
+ */
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+public class BinderFrozenStateChangeNotificationTest {
+ private static final String TAG = BinderFrozenStateChangeNotificationTest.class.getSimpleName();
+
+ private static final String TEST_PACKAGE_NAME_1 =
+ "com.android.frameworks.coretests.bfscctestapp";
+ private static final String TEST_PACKAGE_NAME_2 =
+ "com.android.frameworks.coretests.bdr_helper_app1";
+ private static final String TEST_APP_CMD_SERVICE =
+ TEST_PACKAGE_NAME_1 + ".BfsccTestAppCmdService";
+
+ private static final int CALLBACK_WAIT_TIMEOUT_SECS = 5;
+
+ private IBfsccTestAppCmdService mBfsccTestAppCmdService;
+ private ServiceConnection mTestAppConnection;
+ private Context mContext;
+ private Handler mHandler;
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mHandler = new Handler(Looper.getMainLooper());
+ ((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE)).killUid(
+ mContext.getPackageManager().getPackageUid(TEST_PACKAGE_NAME_1, 0),
+ "Wiping Test Package");
+ mTestAppConnection = bindService();
+ }
+
+ private IBinder getNewRemoteBinder(String testPackage) throws InterruptedException {
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ final AtomicInteger resultCode = new AtomicInteger(Activity.RESULT_CANCELED);
+ final AtomicReference resultExtras = new AtomicReference<>();
+
+ final Intent intent = new Intent(TestCommsReceiver.ACTION_GET_BINDER)
+ .setClassName(testPackage, TestCommsReceiver.class.getName());
+ mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ resultCode.set(getResultCode());
+ resultExtras.set(getResultExtras(true));
+ resultLatch.countDown();
+ }
+ }, mHandler, Activity.RESULT_CANCELED, null, null);
+
+ assertTrue("Request for binder timed out", resultLatch.await(5, TimeUnit.SECONDS));
+ assertEquals(Activity.RESULT_OK, resultCode.get());
+ return resultExtras.get().getBinder(TestCommsReceiver.EXTRA_KEY_BINDER);
+ }
+
+ private ServiceConnection bindService()
+ throws Exception {
+ final CountDownLatch bindLatch = new CountDownLatch(1);
+ ServiceConnection connection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "Service connected");
+ mBfsccTestAppCmdService = IBfsccTestAppCmdService.Stub.asInterface(service);
+ bindLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "Service disconnected");
+ }
+ };
+ mContext.bindService(
+ new Intent().setComponent(
+ new ComponentName(TEST_PACKAGE_NAME_1, TEST_APP_CMD_SERVICE)),
+ connection,
+ Context.BIND_AUTO_CREATE
+ | Context.BIND_NOT_FOREGROUND);
+ if (!bindLatch.await(5, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for the service to bind");
+ }
+ return connection;
+ }
+
+ private void unbindService(ServiceConnection service) {
+ if (service != null) {
+ mContext.unbindService(service);
+ }
+ }
+
+ @Test
+ public void onStateChangeCalled() throws Exception {
+ final LinkedBlockingQueue results = new LinkedBlockingQueue<>();
+ if (createCallback(mBfsccTestAppCmdService.asBinder(), results) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results);
+ freezeApp1();
+ ensureFrozenCallback(results);
+ unfreezeApp1();
+ ensureUnfrozenCallback(results);
+ }
+
+ @Test
+ public void onStateChangeNotCalledAfterCallbackRemoved() throws Exception {
+ final LinkedBlockingQueue results = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback;
+ if ((callback = createCallback(mBfsccTestAppCmdService.asBinder(), results)) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results);
+ mBfsccTestAppCmdService.asBinder().removeFrozenStateChangeCallback(callback);
+ freezeApp1();
+ assertEquals("No more callbacks should be invoked.", 0, results.size());
+ }
+
+ @Test
+ public void multipleCallbacks() throws Exception {
+ final LinkedBlockingQueue results1 = new LinkedBlockingQueue<>();
+ final LinkedBlockingQueue results2 = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback1;
+ if ((callback1 = createCallback(mBfsccTestAppCmdService.asBinder(), results1)) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results1);
+ freezeApp1();
+ ensureFrozenCallback(results1);
+ if (createCallback(mBfsccTestAppCmdService.asBinder(), results2) == null) {
+ return;
+ }
+ ensureFrozenCallback(results2);
+
+ unfreezeApp1();
+ ensureUnfrozenCallback(results1);
+ ensureUnfrozenCallback(results2);
+
+ mBfsccTestAppCmdService.asBinder().removeFrozenStateChangeCallback(callback1);
+ freezeApp1();
+ assertEquals("No more callbacks should be invoked.", 0, results1.size());
+ ensureFrozenCallback(results2);
+ }
+
+ @Test
+ public void onStateChangeCalledWithTheRightBinder() throws Exception {
+ final IBinder binder = mBfsccTestAppCmdService.asBinder();
+ final LinkedBlockingQueue results = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback =
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state) -> results.offer(who);
+ try {
+ binder.addFrozenStateChangeCallback(callback);
+ } catch (UnsupportedOperationException e) {
+ return;
+ }
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ freezeApp1();
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ unfreezeApp1();
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ @After
+ public void tearDown() {
+ if (mTestAppConnection != null) {
+ mContext.unbindService(mTestAppConnection);
+ }
+ }
+
+ private IBinder.IFrozenStateChangeCallback createCallback(IBinder binder, Queue queue)
+ throws RemoteException {
+ try {
+ final IBinder.IFrozenStateChangeCallback callback =
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state) ->
+ queue.offer(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ binder.addFrozenStateChangeCallback(callback);
+ return callback;
+ } catch (UnsupportedOperationException e) {
+ return null;
+ }
+ }
+
+ private void ensureFrozenCallback(LinkedBlockingQueue queue)
+ throws InterruptedException {
+ assertEquals(Boolean.TRUE, queue.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ private void ensureUnfrozenCallback(LinkedBlockingQueue queue)
+ throws InterruptedException {
+ assertEquals(Boolean.FALSE, queue.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ private String executeShellCommand(String cmd) throws Exception {
+ return UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+ }
+
+ private void freezeApp1() throws Exception {
+ executeShellCommand("am freeze " + TEST_PACKAGE_NAME_1);
+ }
+
+ private void freezeApp2() throws Exception {
+ executeShellCommand("am freeze " + TEST_PACKAGE_NAME_2);
+ }
+
+ private void unfreezeApp1() throws Exception {
+ executeShellCommand("am unfreeze " + TEST_PACKAGE_NAME_1);
+ }
+
+ private void unfreezeApp2() throws Exception {
+ executeShellCommand("am unfreeze " + TEST_PACKAGE_NAME_2);
+ }
+}
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index ecd2f76a5160f0b8f574583ed1ee27152b997af8..b157c95a372ec1179ba44e6f091f652e174c7278 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -16,8 +16,11 @@
package android.os.storage;
+import android.content.res.ObbInfo;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ProxyFileDescriptorCallback;
+import android.os.ServiceManager;
import android.system.ErrnoException;
import androidx.test.filters.LargeTest;
@@ -104,7 +107,14 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
public void testMountBadPackageNameObb() throws Exception {
final File file = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
String filePath = file.getAbsolutePath();
- mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ try {
+ mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ fail("mountObb should throw an exception as package name is incorrect");
+ } catch (Exception ex) {
+ assertEquals("Path " + filePath
+ + " does not contain package name " + mContext.getPackageName(),
+ ex.getMessage());
+ }
}
/**
@@ -154,6 +164,48 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
}
}
+ @LargeTest
+ public void testObbInfo_withValidObbInfo_success() throws Exception {
+ final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+ String filePath = file.getAbsolutePath();
+ try {
+ mountObb(filePath);
+ unmountObb(filePath, DONT_FORCE);
+ } catch (Exception ex) {
+ fail("No exception expected, got " + ex.getMessage());
+ }
+ }
+
+ @LargeTest
+ public void testObbInfo_withInvalidObbInfo_exception() throws Exception {
+ final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+ String rawPath = file.getAbsolutePath();
+ String canonicalPath = file.getCanonicalPath();
+
+ ObbInfo obbInfo = ObbInfo.CREATOR.createFromParcel(Parcel.obtain());
+ obbInfo.packageName = "com.android.obbcrash";
+ obbInfo.version = 1;
+ obbInfo.filename = canonicalPath;
+
+ try {
+ IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")).mountObb(
+ rawPath, canonicalPath, new ObbActionListener(), 0, obbInfo);
+ fail("mountObb should throw an exception as package name is incorrect");
+ } catch (SecurityException ex) {
+ assertEquals("Path " + canonicalPath
+ + " does not contain package name " + mContext.getPackageName(),
+ ex.getMessage());
+ }
+ }
+
+ private static class ObbActionListener extends IObbActionListener.Stub {
+ @SuppressWarnings("hiding")
+ @Override
+ public void onObbResult(String filename, int nonce, int status) {
+
+ }
+ }
+
private static class MyThreadFactory implements ThreadFactory {
Thread thread = null;
diff --git a/core/tests/coretests/src/android/tracing/TEST_MAPPING b/core/tests/coretests/src/android/tracing/TEST_MAPPING
new file mode 100644
index 0000000000000000000000000000000000000000..4b7adf92cc03aa1240f03f5e4415f6bb8936bf6b
--- /dev/null
+++ b/core/tests/coretests/src/android/tracing/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksCoreTests_android_tracing",
+ "file_patterns": [".*\\.java"]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 4d9b591c09907d56ea92076f28d926442e5e8b77..00ffda867d6aba9c544766e6bd380da173a1f6b8 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -254,11 +254,8 @@ public class ImeBackAnimationControllerTest {
float progress = 0.5f;
mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
// verify correct ime insets manipulation
- float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
- int expectedInset =
- (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
- eq(Insets.of(0, 0, 0, expectedInset)), eq(1f), anyFloat());
+ eq(Insets.of(0, 0, 0, getImeHeight(progress))), eq(1f), anyFloat());
}
@Test
@@ -268,12 +265,13 @@ public class ImeBackAnimationControllerTest {
WindowInsetsAnimationControlListener animationControlListener = startBackGesture();
// progress back gesture
- mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
+ float progress = 0.5f;
+ mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
// commit back gesture
mBackAnimationController.onBackInvoked();
- // verify setInsetsAndAlpha never called due onReady delayed
+ // verify setInsetsAndAlpha never called due to onReady delayed
verify(mWindowInsetsAnimationController, never()).setInsetsAndAlpha(any(), anyInt(),
anyFloat());
verify(mInsetsController, never()).setPredictiveBackImeHideAnimInProgress(eq(true));
@@ -283,7 +281,7 @@ public class ImeBackAnimationControllerTest {
// verify setInsetsAndAlpha immediately called
verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
- eq(Insets.of(0, 0, 0, IME_HEIGHT)), eq(1f), anyFloat());
+ eq(Insets.of(0, 0, 0, getImeHeight(progress))), eq(1f), anyFloat());
// verify post-commit hide anim has started
verify(mInsetsController, times(1)).setPredictiveBackImeHideAnimInProgress(eq(true));
});
@@ -319,4 +317,9 @@ public class ImeBackAnimationControllerTest {
return animationControlListener.getValue();
}
+
+ private int getImeHeight(float gestureProgress) {
+ float interpolatedProgress = BACK_GESTURE.getInterpolation(gestureProgress);
+ return (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index ce7e85868e8c49c4cbf930af4db7356341200cca..bec8b1f76394a688a8bbb462bb87f7fc226f65ab 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -1022,7 +1022,7 @@ public class InsetsControllerTest {
}
@Test
- public void testImeRequestedVisibleDuringPredictiveBackAnim() {
+ public void testImeRequestedVisibleDuringPredictiveBackAnimWithoutCallback() {
prepareControls();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// show ime as initial state
@@ -1050,6 +1050,42 @@ public class InsetsControllerTest {
});
}
+ @Test
+ public void testImeRequestedInvisibleDuringPredictiveBackAnimWithCallback() {
+ prepareControls();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // set WindowInsetsAnimationCallback on ViewRoot
+ mViewRoot.getView().setWindowInsetsAnimationCallback(
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @Override
+ public WindowInsets onProgress(
+ @NonNull WindowInsets insets,
+ @NonNull List runningAnimations) {
+ return insets;
+ }
+ });
+
+ // show ime as initial state
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+ mController.cancelExistingAnimations(); // fast forward show animation
+ assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+ // start control request (for predictive back animation)
+ WindowInsetsAnimationControlListener listener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+ listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+ ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+ // Verify that onReady is called (after next predraw)
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+ verify(listener).onReady(notNull(), eq(ime()));
+
+ // verify that insets are requested invisible during animation
+ assertFalse(isRequestedVisible(mController, ime()));
+ });
+ }
+
@Test
public void testImeShowRequestCancelsPredictiveBackPostCommitAnim() {
prepareControls();
@@ -1131,6 +1167,37 @@ public class InsetsControllerTest {
});
}
+ @Test
+ public void testPredictiveBackControlRequestCancelledDuringImeHideAnim() {
+ prepareControls();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // show ime as initial state
+ if (!Flags.refactorInsetsController()) {
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+ } else {
+ mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+ }
+ mController.cancelExistingAnimations(); // fast forward show animation
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+ assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+ // start IME hide animation
+ mController.hide(ime(), true /* fromIme */, null /* statsToken */);
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ime()));
+
+ // start control request (for predictive back animation)
+ WindowInsetsAnimationControlListener listener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+ listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+ ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+ // verify that control request is cancelled and animation type remains HIDE
+ verify(listener).onCancelled(any());
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ime()));
+ });
+ }
+
private void waitUntilNextFrame() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/core/tests/coretests/src/android/widget/ChronometerTest.java b/core/tests/coretests/src/android/widget/ChronometerTest.java
index 3c738372377af78a0dbe725bc6ce26ab47a19bfc..cb331176aa31cb8f3eb11bf83ebbfabc15af7cd2 100644
--- a/core/tests/coretests/src/android/widget/ChronometerTest.java
+++ b/core/tests/coretests/src/android/widget/ChronometerTest.java
@@ -17,6 +17,7 @@
package android.widget;
import android.app.Activity;
+import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import androidx.test.filters.LargeTest;
@@ -28,7 +29,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
- * Test {@link DatePicker} focus changes.
+ * Test {@link Chronometer} counting up and down.
*/
@SuppressWarnings("deprecation")
@LargeTest
@@ -50,26 +51,48 @@ public class ChronometerTest extends ActivityInstrumentationTestCase2 ticks = new ArrayList<>();
runOnUiThread(() -> {
mChronometer.setOnChronometerTickListener((chronometer) -> {
ticks.add(chronometer.getText().toString());
latch.countDown();
try {
- Thread.sleep(500);
+ Thread.sleep(250);
} catch (InterruptedException e) {
}
});
mChronometer.start();
});
- assertTrue(latch.await(6, TimeUnit.SECONDS));
- assertTrue(ticks.size() >= 5);
+ assertTrue(latch.await(5500, TimeUnit.MILLISECONDS));
assertEquals("00:00", ticks.get(0));
assertEquals("00:01", ticks.get(1));
assertEquals("00:02", ticks.get(2));
assertEquals("00:03", ticks.get(3));
assertEquals("00:04", ticks.get(4));
+ assertEquals("00:05", ticks.get(5));
+ }
+
+ public void testChronometerCountDown() throws Throwable {
+ final CountDownLatch latch = new CountDownLatch(5);
+ ArrayList ticks = new ArrayList<>();
+ runOnUiThread(() -> {
+ mChronometer.setBase(SystemClock.elapsedRealtime() + 3_000);
+ mChronometer.setCountDown(true);
+ mChronometer.post(() -> {
+ mChronometer.setOnChronometerTickListener((chronometer) -> {
+ ticks.add(chronometer.getText().toString());
+ latch.countDown();
+ });
+ mChronometer.start();
+ });
+ });
+ assertTrue(latch.await(4500, TimeUnit.MILLISECONDS));
+ assertEquals("00:02", ticks.get(0));
+ assertEquals("00:01", ticks.get(1));
+ assertEquals("00:00", ticks.get(2));
+ assertEquals("−00:01", ticks.get(3));
+ assertEquals("−00:02", ticks.get(4));
}
private void runOnUiThread(Runnable runnable) throws InterruptedException {
diff --git a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
index bcf1053e8ddd083110acd45f21b2155d8a154adb..3e76977c99fab4a04cc7c7d6f7f386cc892a9950 100644
--- a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -40,6 +41,9 @@ import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.textclassifier.TextClassification;
@@ -47,9 +51,12 @@ import android.view.textclassifier.TextClassification;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.verification.VerificationMode;
/**
* TextViewTest tests {@link TextView}.
@@ -86,6 +93,10 @@ public class TextViewContextMenuTest {
private SelectionActionModeHelper mMockHelper;
+ @ClassRule public static final SetFlagsRule.ClassRule SET_FLAGS_CLASS_RULE =
+ new SetFlagsRule.ClassRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = SET_FLAGS_CLASS_RULE.createSetFlagsRule();
+
@Before
public void setUp() {
mMockHelper = mock(SelectionActionModeHelper.class);
@@ -234,6 +245,7 @@ public class TextViewContextMenuTest {
}
@Test
+ @DisableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
public void testAutofillMenuItemEnabledWhenNoTextSelected() {
ContextMenu menu = mock(ContextMenu.class);
MenuItem mockMenuItem = newMockMenuItem();
@@ -254,6 +266,7 @@ public class TextViewContextMenuTest {
}
@Test
+ @DisableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
public void testAutofillMenuItemNotEnabledWhenTextSelected() {
ContextMenu menu = mock(ContextMenu.class);
MenuItem mockMenuItem = newMockMenuItem();
@@ -271,4 +284,147 @@ public class TextViewContextMenuTest {
verify(menu).add(anyInt(), eq(TextView.ID_AUTOFILL), anyInt(), anyInt());
verify(mockAutofillMenuItem).setEnabled(false);
}
+
+ private interface EditTextSetup {
+ void run(EditText et);
+ }
+
+ private void verifyMenuItemNotAdded(EditTextSetup setup, int id, VerificationMode times) {
+ ContextMenu menu = mock(ContextMenu.class);
+ MenuItem mockMenuItem = newMockMenuItem();
+ when(menu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mockMenuItem);
+ EditText et = spy(new EditText(getInstrumentation().getContext()));
+ setup.run(et);
+ Editor editor = new Editor(et);
+ editor.setTextContextMenuItems(menu);
+ verify(menu, times).add(anyInt(), eq(id), anyInt(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuUndoNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canUndo(),
+ TextView.ID_UNDO, never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuUndoAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canUndo(), TextView.ID_UNDO,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuRedoNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canRedo(), TextView.ID_REDO,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuRedoAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canRedo(), TextView.ID_REDO,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuCutNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canCut(), TextView.ID_CUT,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuCutAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canCut(), TextView.ID_CUT,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuCopyNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canCopy(), TextView.ID_COPY,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuCopyAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canCopy(), TextView.ID_COPY,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuPasteNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canPaste(), TextView.ID_PASTE,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuPasteAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canPaste(), TextView.ID_PASTE,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuPasteAsPlaintextNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canPasteAsPlainText(),
+ TextView.ID_PASTE_AS_PLAIN_TEXT, never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuPasteAsPlaintextAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canPasteAsPlainText(),
+ TextView.ID_PASTE_AS_PLAIN_TEXT, times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuSelectAllNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canSelectAllText(),
+ TextView.ID_SELECT_ALL, never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuSelectAllAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canSelectAllText(),
+ TextView.ID_SELECT_ALL, times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuShareNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canShare(), TextView.ID_SHARE,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuShareAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canShare(), TextView.ID_SHARE,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuAutofillNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canRequestAutofill(),
+ TextView.ID_AUTOFILL, never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuAutofillNotAddedWhenUnavailableBecauseTextSelected() {
+ verifyMenuItemNotAdded((spy) -> {
+ doReturn(true).when(spy).canRequestAutofill();
+ doReturn("test").when(spy).getSelectedText();
+ }, TextView.ID_AUTOFILL, never());
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 499caf5e12d3f36906467347d8d49e7979b5e0a6..c3a5b19c94422670129e49fd421a2adf1cf89aa8 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -359,7 +359,7 @@ public class FrameTrackerTest {
tracker.end(FrameTracker.REASON_END_NORMAL);
// Send incomplete callback for 102L
- sendSfFrame(tracker, 102L, JANK_NONE);
+ sendSfFrame(tracker, 4, 102L, JANK_NONE);
// Send janky but complete callbck fo 103L
sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
@@ -629,7 +629,7 @@ public class FrameTrackerTest {
if (!tracker.mSurfaceOnly) {
sendHwuiFrame(tracker, durationMillis, vsyncId, firstWindowFrame);
}
- sendSfFrame(tracker, vsyncId, jankType);
+ sendSfFrame(tracker, durationMillis, vsyncId, jankType);
}
private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId,
@@ -645,11 +645,13 @@ public class FrameTrackerTest {
captor.getValue().run();
}
- private void sendSfFrame(FrameTracker tracker, long vsyncId, @JankType int jankType) {
+ private void sendSfFrame(
+ FrameTracker tracker, long durationMillis, long vsyncId, @JankType int jankType) {
final ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class);
doNothing().when(tracker).postCallback(captor.capture());
mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
- new JankData(vsyncId, jankType, FRAME_TIME_60Hz)
+ new JankData(vsyncId, jankType, FRAME_TIME_60Hz, FRAME_TIME_60Hz,
+ TimeUnit.MILLISECONDS.toNanos(durationMillis))
});
captor.getValue().run();
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 66de3d7f24f9835b3cb94aedd4b67b5852585cb5..397cdcf6acdd22201454e88b3f6be45115676917 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -124,6 +124,16 @@ public class BinderDeathDispatcherTest {
return this;
}
+ @Override
+ public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ }
+
+ @Override
+ public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+ return false;
+ }
+
public void die() {
isAlive = false;
if (mRecipient != null) {
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
index b183ecb50591b0e26cc34ec195f0eb4b99b9ee18..149e132a0df4d44c3f55ab55fc7a33019b94c480 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.UserHandle;
@@ -69,22 +70,22 @@ public class StatusBarIconTest {
assertThat(copy.preloadedIcon).isEqualTo(original.preloadedIcon);
}
-
private static StatusBarIcon newStatusBarIcon() {
final UserHandle dummyUserHandle = UserHandle.of(100);
final String dummyIconPackageName = "com.android.internal.statusbar.test";
- final int dummyIconId = 123;
+ final Icon dummyIcon = Icon.createWithResource(dummyIconPackageName, 123);
final int dummyIconLevel = 1;
final int dummyIconNumber = 2;
final CharSequence dummyIconContentDescription = "dummyIcon";
return new StatusBarIcon(
- dummyIconPackageName,
dummyUserHandle,
- dummyIconId,
+ dummyIconPackageName,
+ dummyIcon,
dummyIconLevel,
dummyIconNumber,
dummyIconContentDescription,
- StatusBarIcon.Type.SystemIcon);
+ StatusBarIcon.Type.SystemIcon,
+ StatusBarIcon.Shape.FIXED_SPACE);
}
private static void assertSerializableFieldsEqual(StatusBarIcon copy, StatusBarIcon original) {
@@ -96,6 +97,7 @@ public class StatusBarIconTest {
assertThat(copy.number).isEqualTo(original.number);
assertThat(copy.contentDescription).isEqualTo(original.contentDescription);
assertThat(copy.type).isEqualTo(original.type);
+ assertThat(copy.shape).isEqualTo(original.shape);
}
private static StatusBarIcon parcelAndUnparcel(StatusBarIcon original) {
diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
index 5f6eaf96a846e9a9f5b21f088740fc85cbf0618b..e11bc552cf1508a5754330e344ab2ac883d9f011 100644
--- a/core/tests/hdmitests/Android.bp
+++ b/core/tests/hdmitests/Android.bp
@@ -37,3 +37,10 @@ android_test {
certificate: "platform",
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "HdmiCecTests_hardware_hdmi",
+ base: "HdmiCecTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.hardware.hdmi"],
+}
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index aca52a87065519e09ae28ee79e64fce46994163a..7fd813a7421a3a1bbbed89c18c7eb00a8ba44f09 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -63,3 +63,13 @@ android_test {
certificate: "platform",
}
+
+test_module_config {
+ name: "FrameworksMockingCoreTests_os_bundlerecyclingtest",
+ base: "FrameworksMockingCoreTests",
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+ include_filters: ["android.os.BundleRecyclingTest"],
+}
diff --git a/core/tests/timetests/Android.bp b/core/tests/timetests/Android.bp
index 51181a8a7b13246107701ff63c7822244d3d7766..c33d5ee5008bb0bcf34ae2825d8833c93aaa26ba 100644
--- a/core/tests/timetests/Android.bp
+++ b/core/tests/timetests/Android.bp
@@ -23,3 +23,17 @@ android_test {
certificate: "platform",
test_suites: ["device-tests"],
}
+
+test_module_config {
+ name: "FrameworksTimeCoreTests_android_app",
+ base: "FrameworksTimeCoreTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.app."],
+}
+
+test_module_config {
+ name: "FrameworksTimeCoreTests_android_service",
+ base: "FrameworksTimeCoreTests",
+ test_suites: ["device-tests"],
+ include_filters: ["android.service."],
+}
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 4f76dd636c30b193d19744b4d4b3a3abcb06825d..f5b04ee759a5c0fbfad62df2c32aac8bba370709 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -429,6 +429,86 @@ public class VibrationEffectTest {
assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
}
+ @Test
+ public void cropToLength_waveform_underLength() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 1, 2},
+ /* repeatIndex= */ -1);
+ VibrationEffect result = effect.cropToLengthOrNull(5);
+
+ assertThat(result).isEqualTo(effect); // unchanged
+ }
+
+ @Test
+ public void cropToLength_waveform_overLength() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+ /* repeatIndex= */ -1);
+ VibrationEffect result = effect.cropToLengthOrNull(4);
+
+ assertThat(result).isEqualTo(VibrationEffect.createWaveform(
+ new long[]{0, 1, 2, 3},
+ -1));
+ }
+
+ @Test
+ public void cropToLength_waveform_repeating() {
+ // repeating waveforms cannot be truncated
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+ /* repeatIndex= */ 2);
+ VibrationEffect result = effect.cropToLengthOrNull(3);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void cropToLength_waveform_withAmplitudes() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+ /* amplitudes= */ new int[]{10, 20, 40, 10, 20, 40, 10},
+ /* repeatIndex= */ -1);
+ VibrationEffect result = effect.cropToLengthOrNull(3);
+
+ assertThat(result).isEqualTo(VibrationEffect.createWaveform(
+ new long[]{0, 1, 2},
+ new int[]{10, 20, 40},
+ -1));
+ }
+
+ @Test
+ public void cropToLength_composed() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .compose();
+ VibrationEffect result = effect.cropToLengthOrNull(1);
+
+ assertThat(result).isNotNull();
+ assertThat(result).isEqualTo(VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose());
+ }
+
+ @Test
+ public void cropToLength_composed_repeating() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .repeatEffectIndefinitely(TEST_ONE_SHOT)
+ .compose();
+ assertThat(effect.cropToLengthOrNull(1)).isNull();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void cropToLength_vendorEffect() {
+ PersistableBundle vendorData = new PersistableBundle();
+ vendorData.putInt("key", 1);
+ VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData);
+
+ assertThat(effect.cropToLengthOrNull(2)).isNull();
+ }
+
@Test
public void getRingtones_noPrebakedRingtones() {
Resources r = mockRingtoneResources(new String[0]);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9a55b80f37e27f199923a9559edc643e64500dbe..880f30c6cdc0bbf58ab2a89e6f2b7f7cd96e4a55 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -580,6 +580,11 @@ applications that come with the platform
+
+
+
+
+
diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp
index cbaac21aa10b96d09428e9986aad7e9793c82c51..4edf52bae16123da7469a7e0c94c00139e098f73 100644
--- a/data/fonts/Android.bp
+++ b/data/fonts/Android.bp
@@ -71,16 +71,9 @@ prebuilt_fonts_xml {
},
}
-// TODO(nona): Change this to use generate_font_fallback to be able to generate XML from
-// per family JSON config
-prebuilt_fonts_xml {
+prebuilt_etc {
name: "font_fallback.xml",
- src: "font_fallback.xml",
- soong_config_variables: {
- use_var_font: {
- src: "font_fallback_cjkvf.xml",
- },
- },
+ src: ":generate_font_fallback",
}
/////////////////////////////////
diff --git a/data/keyboards/Vendor_0957_Product_0033.idc b/data/keyboards/Vendor_0957_Product_0033.idc
new file mode 100644
index 0000000000000000000000000000000000000000..7dfbe2cced34a551546d0508e3c95680fefdecbc
--- /dev/null
+++ b/data/keyboards/Vendor_0957_Product_0033.idc
@@ -0,0 +1,23 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Input Device Configuration file for Google Reference RCU Remote.
+# PID 0033 is for new G20 with start button.
+
+# Basic Parameters
+keyboard.layout = Vendor_0957_Product_0031
+# The reason why we set is follow https://docs.partner.android.com/tv/build/gtv/boot-resume
+keyboard.doNotWakeByDefault = 1
+audio.mic = 1
diff --git a/graphics/java/android/graphics/Matrix44.java b/graphics/java/android/graphics/Matrix44.java
index a99e20101c3bb9a6023ad66859f9548a0bf59c53..683f6149a203d471619cccd0ec7e9ec5ff395010 100644
--- a/graphics/java/android/graphics/Matrix44.java
+++ b/graphics/java/android/graphics/Matrix44.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.graphics.hwui.flags.Flags;
@@ -30,6 +31,7 @@ import java.util.Arrays;
* in row-major order. The values and operations are treated as column vectors.
*/
@FlaggedApi(Flags.FLAG_MATRIX_44)
+@RavenwoodKeepWholeClass
public class Matrix44 {
final float[] mBackingArray;
/**
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 618e6dcc4433422c8ef560a796374054dadb2c11..c7b89412cc47926c89682b847f200496b47fb0b1 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.drawable.Drawable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -35,6 +36,7 @@ import java.lang.annotation.RetentionPolicy;
* @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider)
* @see Drawable#getOutline(Outline)
*/
+@RavenwoodKeepWholeClass
public final class Outline {
private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY;
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index 748d66cb5f6c1afed89d207a1dca5b54f7d6c826..76c1715475aebb87d3d706773dacc628e1b56b89 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
/**
* A {@link Parcelable} wrapper for a {@link ColorSpace}. In order to enable parceling, the
@@ -27,6 +28,7 @@ import android.os.Parcelable;
* {@link ColorSpace.Rgb} instance that has an ICC parametric transfer function as returned by
* {@link ColorSpace.Rgb#getTransferParameters()}.
*/
+@RavenwoodKeepWholeClass
public final class ParcelableColorSpace implements Parcelable {
private final ColorSpace mColorSpace;
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 3ec5b9cc7dae1fd298059cbb07c7e36ab62cc1df..a872e03db3b5c4439706d18c1d74977378db9bce 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -17,10 +17,12 @@
package android.graphics;
import android.annotation.IntDef;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+@RavenwoodKeepWholeClass
public class PixelFormat {
/** @hide */
@IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE})
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index ba5628cd2bc150dcc125290f63a9c921c558bb39..b7bf0553bcc6a0cd52ab61e4760e4ddec0ffabc4 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -182,10 +182,8 @@ public class FontCustomizationParser {
// For ignoring the customization, consume the new-locale-family element but don't
// register any customizations.
- if (com.android.text.flags.Flags.vendorCustomLocaleFallback()) {
- outCustomization.add(new FontConfig.Customization.LocaleFallback(
- Locale.forLanguageTag(lang), intOp, family));
- }
+ outCustomization.add(new FontConfig.Customization.LocaleFallback(
+ Locale.forLanguageTag(lang), intOp, family));
} else {
throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
index 69a68c85a2fe308b466abb291add80040dd5fcf5..4ce294213526f7d383d64657d4963c95a5c803f5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
@@ -24,6 +24,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -57,7 +58,7 @@ class BackupHelper {
void scheduleBackup() {
if (!mBackupIdlerScheduled) {
mBackupIdlerScheduled = true;
- Looper.myQueue().addIdleHandler(mBackupIdler);
+ Looper.getMainLooper().getQueue().addIdleHandler(mBackupIdler);
}
}
@@ -80,9 +81,14 @@ class BackupHelper {
}
if (DEBUG) Log.d(TAG, "Start to back up " + taskContainers);
+ final List parcelableTaskContainerDataList = new ArrayList<>(
+ taskContainers.size());
+ for (TaskContainer taskContainer : taskContainers) {
+ parcelableTaskContainerDataList.add(taskContainer.getParcelableData());
+ }
final Bundle state = new Bundle();
- state.setClassLoader(TaskContainer.class.getClassLoader());
- state.putParcelableList(KEY_TASK_CONTAINERS, taskContainers);
+ state.setClassLoader(ParcelableTaskContainerData.class.getClassLoader());
+ state.putParcelableList(KEY_TASK_CONTAINERS, parcelableTaskContainerDataList);
mController.setSavedState(state);
}
@@ -91,10 +97,12 @@ class BackupHelper {
return;
}
- final List taskContainers = savedState.getParcelableArrayList(
- KEY_TASK_CONTAINERS, TaskContainer.class);
- for (TaskContainer taskContainer : taskContainers) {
- if (DEBUG) Log.d(TAG, "restore task " + taskContainer.getTaskId());
+ final List parcelableTaskContainerDataList =
+ savedState.getParcelableArrayList(KEY_TASK_CONTAINERS,
+ ParcelableTaskContainerData.class);
+ for (ParcelableTaskContainerData data : parcelableTaskContainerDataList) {
+ final TaskContainer taskContainer = new TaskContainer(data, mController);
+ if (DEBUG) Log.d(TAG, "Restoring task " + taskContainer.getTaskId());
// TODO(b/289875940): implement the TaskContainer restoration.
}
}
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 1eb95c1efb0843148776288ffd93df4415e77ced..9ea2943bc6daddefa14414f4608b13fdffe88989 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -70,6 +70,10 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@NonNull
private final TaskFragmentCallback mCallback;
+ @VisibleForTesting
+ @Nullable
+ TaskFragmentAnimationController mAnimationController;
+
/**
* Callback that notifies the controller about changes to task fragments.
*/
@@ -87,6 +91,25 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
mCallback = callback;
}
+ @Override
+ public void unregisterOrganizer() {
+ if (mAnimationController != null) {
+ mAnimationController.unregisterRemoteAnimations();
+ mAnimationController = null;
+ }
+ super.unregisterOrganizer();
+ }
+
+ /**
+ * Overrides the animation for transitions of embedded activities organized by this organizer.
+ */
+ void overrideSplitAnimation() {
+ if (mAnimationController == null) {
+ mAnimationController = new TaskFragmentAnimationController(this);
+ }
+ mAnimationController.registerRemoteAnimations();
+ }
+
/**
* Starts a new Activity and puts it into split with an existing Activity side-by-side.
* @param launchingFragmentToken token for the launching TaskFragment. If it exists, it will
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
new file mode 100644
index 0000000000000000000000000000000000000000..817cfce69b2e15457e8d8cf60697fb753aeed0be
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * This class holds the Parcelable data of a {@link SplitContainer}.
+ */
+class ParcelableSplitContainerData implements Parcelable {
+
+ /**
+ * A reference to the target {@link SplitContainer} that owns the data. This will not be
+ * parcelled and will be {@code null} when the data is created from a parcel.
+ */
+ @Nullable
+ final SplitContainer mSplitContainer;
+
+ @NonNull
+ final IBinder mToken;
+
+ @NonNull
+ private final IBinder mPrimaryContainerToken;
+
+ @NonNull
+ private final IBinder mSecondaryContainerToken;
+
+ // TODO(b/289875940): making this as non-null once the tag can be auto-generated from the rule.
+ @Nullable
+ final String mSplitRuleTag;
+
+ /**
+ * Whether the selection of which container is primary can be changed at runtime. Runtime
+ * updates is currently possible only for {@link SplitPinContainer}
+ *
+ * @see SplitPinContainer
+ */
+ final boolean mIsPrimaryContainerMutable;
+
+ ParcelableSplitContainerData(@NonNull SplitContainer splitContainer, @NonNull IBinder token,
+ @NonNull IBinder primaryContainerToken, @NonNull IBinder secondaryContainerToken,
+ @Nullable String splitRuleTag, boolean isPrimaryContainerMutable) {
+ mSplitContainer = splitContainer;
+ mToken = token;
+ mPrimaryContainerToken = primaryContainerToken;
+ mSecondaryContainerToken = secondaryContainerToken;
+ mSplitRuleTag = splitRuleTag;
+ mIsPrimaryContainerMutable = isPrimaryContainerMutable;
+ }
+
+ private ParcelableSplitContainerData(Parcel in) {
+ mSplitContainer = null;
+ mToken = in.readStrongBinder();
+ mPrimaryContainerToken = in.readStrongBinder();
+ mSecondaryContainerToken = in.readStrongBinder();
+ mSplitRuleTag = in.readString();
+ mIsPrimaryContainerMutable = in.readBoolean();
+ }
+
+ public static final Creator CREATOR = new Creator<>() {
+ @Override
+ public ParcelableSplitContainerData createFromParcel(Parcel in) {
+ return new ParcelableSplitContainerData(in);
+ }
+
+ @Override
+ public ParcelableSplitContainerData[] newArray(int size) {
+ return new ParcelableSplitContainerData[size];
+ }
+ };
+
+ @NonNull
+ private IBinder getPrimaryContainerToken() {
+ return mSplitContainer != null ? mSplitContainer.getPrimaryContainer().getToken()
+ : mPrimaryContainerToken;
+ }
+
+ @NonNull
+ private IBinder getSecondaryContainerToken() {
+ return mSplitContainer != null ? mSplitContainer.getSecondaryContainer().getToken()
+ : mSecondaryContainerToken;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mToken);
+ dest.writeStrongBinder(getPrimaryContainerToken());
+ dest.writeStrongBinder(getSecondaryContainerToken());
+ dest.writeString(mSplitRuleTag);
+ dest.writeBoolean(mIsPrimaryContainerMutable);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java
new file mode 100644
index 0000000000000000000000000000000000000000..7377d005cda493de149688b2fa947467f4c976c1
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class holds the Parcelable data of a {@link TaskContainer}.
+ */
+class ParcelableTaskContainerData implements Parcelable {
+
+ /**
+ * A reference to the target {@link TaskContainer} that owns the data. This will not be
+ * parcelled and will be {@code null} when the data is created from a parcel.
+ */
+ @Nullable
+ final TaskContainer mTaskContainer;
+
+ /**
+ * The unique task id.
+ */
+ final int mTaskId;
+
+ /**
+ * The parcelable data of the active TaskFragmentContainers in this Task.
+ * Note that this will only be populated before parcelling, and will not be copied when
+ * making a new instance copy.
+ */
+ @NonNull
+ private final List
+ mParcelableTaskFragmentContainerDataList = new ArrayList<>();
+
+ /**
+ * The parcelable data of the SplitContainers in this Task.
+ * Note that this will only be populated before parcelling, and will not be copied when
+ * making a new instance copy.
+ */
+ @NonNull
+ private final List mParcelableSplitContainerDataList =
+ new ArrayList<>();
+
+ ParcelableTaskContainerData(int taskId, @NonNull TaskContainer taskContainer) {
+ if (taskId == INVALID_TASK_ID) {
+ throw new IllegalArgumentException("Invalid Task id");
+ }
+
+ mTaskId = taskId;
+ mTaskContainer = taskContainer;
+ }
+
+ ParcelableTaskContainerData(@NonNull ParcelableTaskContainerData data,
+ @NonNull TaskContainer taskContainer) {
+ mTaskId = data.mTaskId;
+ mTaskContainer = taskContainer;
+ }
+
+ private ParcelableTaskContainerData(Parcel in) {
+ mTaskId = in.readInt();
+ mTaskContainer = null;
+ in.readParcelableList(mParcelableTaskFragmentContainerDataList,
+ ParcelableTaskFragmentContainerData.class.getClassLoader(),
+ ParcelableTaskFragmentContainerData.class);
+ in.readParcelableList(mParcelableSplitContainerDataList,
+ ParcelableSplitContainerData.class.getClassLoader(),
+ ParcelableSplitContainerData.class);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mTaskId);
+ dest.writeParcelableList(getParcelableTaskFragmentContainerDataList(), flags);
+ dest.writeParcelableList(getParcelableSplitContainerDataList(), flags);
+ }
+
+ @NonNull
+ List extends ParcelableTaskFragmentContainerData>
+ getParcelableTaskFragmentContainerDataList() {
+ return mTaskContainer != null ? mTaskContainer.getParcelableTaskFragmentContainerDataList()
+ : mParcelableTaskFragmentContainerDataList;
+ }
+
+ @NonNull
+ List extends ParcelableSplitContainerData> getParcelableSplitContainerDataList() {
+ return mTaskContainer != null ? mTaskContainer.getParcelableSplitContainerDataList()
+ : mParcelableSplitContainerDataList;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator CREATOR = new Creator<>() {
+ @Override
+ public ParcelableTaskContainerData createFromParcel(Parcel in) {
+ return new ParcelableTaskContainerData(in);
+ }
+
+ @Override
+ public ParcelableTaskContainerData[] newArray(int size) {
+ return new ParcelableTaskContainerData[size];
+ }
+ };
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
new file mode 100644
index 0000000000000000000000000000000000000000..a79a89a210ac321ade8c4094120bfac81a4c0043
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * This class holds the Parcelable data of a {@link TaskFragmentContainer}.
+ */
+class ParcelableTaskFragmentContainerData implements Parcelable {
+
+ /**
+ * Client-created token that uniquely identifies the task fragment container instance.
+ */
+ @NonNull
+ final IBinder mToken;
+
+ /**
+ * The tag specified in launch options. {@code null} if this taskFragment container is not an
+ * overlay container.
+ */
+ @Nullable
+ final String mOverlayTag;
+
+ /**
+ * The associated {@link Activity#getActivityToken()} of the overlay container.
+ * Must be {@code null} for non-overlay container.
+ *
+ * If an overlay container is associated with an activity, this overlay container will be
+ * dismissed when the associated activity is destroyed. If the overlay container is visible,
+ * activity will be launched on top of the overlay container and expanded to fill the parent
+ * container.
+ */
+ @Nullable
+ final IBinder mAssociatedActivityToken;
+
+ /**
+ * Bounds that were requested last via {@link android.window.WindowContainerTransaction}.
+ */
+ @NonNull
+ final Rect mLastRequestedBounds;
+
+ ParcelableTaskFragmentContainerData(@NonNull IBinder token, @Nullable String overlayTag,
+ @Nullable IBinder associatedActivityToken) {
+ mToken = token;
+ mOverlayTag = overlayTag;
+ mAssociatedActivityToken = associatedActivityToken;
+ mLastRequestedBounds = new Rect();
+ }
+
+ private ParcelableTaskFragmentContainerData(Parcel in) {
+ mToken = in.readStrongBinder();
+ mOverlayTag = in.readString();
+ mAssociatedActivityToken = in.readStrongBinder();
+ mLastRequestedBounds = in.readTypedObject(Rect.CREATOR);
+ }
+
+ public static final Creator CREATOR = new Creator<>() {
+ @Override
+ public ParcelableTaskFragmentContainerData createFromParcel(Parcel in) {
+ return new ParcelableTaskFragmentContainerData(in);
+ }
+
+ @Override
+ public ParcelableTaskFragmentContainerData[] newArray(int size) {
+ return new ParcelableTaskFragmentContainerData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mToken);
+ dest.writeString(mOverlayTag);
+ dest.writeStrongBinder(mAssociatedActivityToken);
+ dest.writeTypedObject(mLastRequestedBounds, flags);
+ }
+
+}
+
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 39cfacec844753c4666ce500d6ac0937dd658010..6d436ec01d98b68051f4b09568903fe82c7ef8d6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -32,6 +32,8 @@ import androidx.window.extensions.core.util.function.Function;
* Client-side descriptor of a split that holds two containers.
*/
class SplitContainer {
+ @NonNull
+ private final ParcelableSplitContainerData mParcelableData;
@NonNull
private TaskFragmentContainer mPrimaryContainer;
@NonNull
@@ -44,16 +46,6 @@ class SplitContainer {
/** @see SplitContainer#getDefaultSplitAttributes() */
@NonNull
private SplitAttributes mDefaultSplitAttributes;
- @NonNull
- private final IBinder mToken;
-
- /**
- * Whether the selection of which container is primary can be changed at runtime. Runtime
- * updates is currently possible only for {@link SplitPinContainer}
- *
- * @see SplitPinContainer
- */
- private final boolean mIsPrimaryContainerMutable;
SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
@NonNull Activity primaryActivity,
@@ -69,13 +61,14 @@ class SplitContainer {
@NonNull TaskFragmentContainer secondaryContainer,
@NonNull SplitRule splitRule,
@NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) {
+ mParcelableData = new ParcelableSplitContainerData(this, new Binder("SplitContainer"),
+ primaryContainer.getToken(), secondaryContainer.getToken(), splitRule.getTag(),
+ isPrimaryContainerMutable);
mPrimaryContainer = primaryContainer;
mSecondaryContainer = secondaryContainer;
mSplitRule = splitRule;
mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
mCurrentSplitAttributes = splitAttributes;
- mToken = new Binder("SplitContainer");
- mIsPrimaryContainerMutable = isPrimaryContainerMutable;
if (shouldFinishPrimaryWithSecondary(splitRule)) {
if (mPrimaryContainer.getRunningActivityCount() == 1
@@ -94,7 +87,7 @@ class SplitContainer {
}
void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) {
- if (!mIsPrimaryContainerMutable) {
+ if (!mParcelableData.mIsPrimaryContainerMutable) {
throw new IllegalStateException("Cannot update primary TaskFragmentContainer");
}
mPrimaryContainer = primaryContainer;
@@ -150,7 +143,12 @@ class SplitContainer {
@NonNull
IBinder getToken() {
- return mToken;
+ return mParcelableData.mToken;
+ }
+
+ @NonNull
+ ParcelableSplitContainerData getParcelableData() {
+ return mParcelableData;
}
/**
@@ -201,7 +199,7 @@ class SplitContainer {
return null;
}
return new SplitInfo(primaryActivityStack, secondaryActivityStack,
- mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken));
+ mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mParcelableData.mToken));
}
static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
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 24b56ae471e523c812e9e4588cd30c3c278be1bf..f2f2b7ea71741c0692a3607ee13c90e561734582 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -58,18 +58,22 @@ import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityOptions;
import android.app.ActivityThread;
+import android.app.AppGlobals;
import android.app.Application;
import android.app.Instrumentation;
import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -116,6 +120,7 @@ import java.util.function.BiConsumer;
public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
ActivityEmbeddingComponent, DividerPresenter.DragEventCallback {
static final String TAG = "SplitController";
+ static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
// TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
// association. It's not set in WM Extensions nor Wm Jetpack library currently.
@@ -919,7 +924,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
- final List containers = taskContainer.getTaskFragmentContainers();
+ final List containers
+ = new ArrayList<>(taskContainer.getTaskFragmentContainers());
for (int i = containers.size() - 1; i >= 0; i--) {
final TaskFragmentContainer container = containers.get(i);
// Wait until onTaskFragmentAppeared to update new container.
@@ -3307,4 +3313,17 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
transactionRecord.apply(false /* shouldApplyIndependently */);
}
}
+
+ // TODO(b/207070762): cleanup with legacy app transition
+ private static boolean getShellTransitEnabled() {
+ try {
+ if (AppGlobals.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ return SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Error getting system features");
+ }
+ return true;
+ }
}
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 fb8efc4ad4901a2383d9666ee5d86604218472ec..abc7b291fc32bdab0d664c75534dbbe86629ea96 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -169,12 +169,17 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
mController = controller;
final Bundle outSavedState = new Bundle();
if (Flags.aeBackStackRestore()) {
- outSavedState.setClassLoader(TaskContainer.class.getClassLoader());
+ outSavedState.setClassLoader(ParcelableTaskContainerData.class.getClassLoader());
registerOrganizer(false /* isSystemOrganizer */, outSavedState);
} else {
registerOrganizer();
}
mBackupHelper = new BackupHelper(controller, outSavedState);
+ if (!SplitController.ENABLE_SHELL_TRANSITIONS) {
+ // TODO(b/207070762): cleanup with legacy app transition
+ // Animation will be handled by WM Shell when Shell transition is enabled.
+ overrideSplitAnimation();
+ }
}
void scheduleBackup() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 5795e8da18c2138f419598aea86a32afff50124c..608a3bee7509bfd2273be5e69552d3cd3278990c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -16,7 +16,6 @@
package androidx.window.extensions.embedding;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
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;
@@ -32,8 +31,6 @@ import android.app.WindowConfiguration.WindowingMode;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -57,11 +54,12 @@ import java.util.List;
import java.util.Set;
/** Represents TaskFragments and split pairs below a Task. */
-class TaskContainer implements Parcelable {
+class TaskContainer {
private static final String TAG = TaskContainer.class.getSimpleName();
- /** The unique task id. */
- private final int mTaskId;
+ /** Parcelable data of this TaskContainer. */
+ @NonNull
+ private final ParcelableTaskContainerData mParcelableTaskContainerData;
/** Active TaskFragments in this Task. */
@NonNull
@@ -130,16 +128,15 @@ class TaskContainer implements Parcelable {
* @param splitController The {@link SplitController}.
*/
TaskContainer(int taskId, @NonNull Activity activityInTask,
- @Nullable SplitController splitController) {
- if (taskId == INVALID_TASK_ID) {
- throw new IllegalArgumentException("Invalid Task id");
- }
- mTaskId = taskId;
+ @NonNull SplitController splitController) {
+ mParcelableTaskContainerData = new ParcelableTaskContainerData(taskId, this);
+
final TaskProperties taskProperties = TaskProperties
.getTaskPropertiesFromActivity(activityInTask);
mInfo = new TaskFragmentParentInfo(
taskProperties.getConfiguration(),
taskProperties.getDisplayId(),
+ taskId,
// Note that it is always called when there's a new Activity is started, which
// implies the host task is visible and has an activity in the task.
true /* visible */,
@@ -148,8 +145,44 @@ class TaskContainer implements Parcelable {
mSplitController = splitController;
}
+ /** This is only used when restoring it from a {@link ParcelableTaskContainerData}. */
+ TaskContainer(@NonNull ParcelableTaskContainerData data,
+ @NonNull SplitController splitController) {
+ mParcelableTaskContainerData = new ParcelableTaskContainerData(data, this);
+ mSplitController = splitController;
+ for (ParcelableTaskFragmentContainerData tfData :
+ data.getParcelableTaskFragmentContainerDataList()) {
+ final TaskFragmentContainer container =
+ new TaskFragmentContainer(tfData, splitController, this);
+ mContainers.add(container);
+ }
+ }
+
+ @NonNull
+ ParcelableTaskContainerData getParcelableData() {
+ return mParcelableTaskContainerData;
+ }
+
+ @NonNull
+ List getParcelableTaskFragmentContainerDataList() {
+ final List data = new ArrayList<>(mContainers.size());
+ for (TaskFragmentContainer container : mContainers) {
+ data.add(container.getParcelableData());
+ }
+ return data;
+ }
+
+ @NonNull
+ List getParcelableSplitContainerDataList() {
+ final List data = new ArrayList<>(mSplitContainers.size());
+ for (SplitContainer splitContainer : mSplitContainers) {
+ data.add(splitContainer.getParcelableData());
+ }
+ return data;
+ }
+
int getTaskId() {
- return mTaskId;
+ return mParcelableTaskContainerData.mTaskId;
}
int getDisplayId() {
@@ -162,7 +195,8 @@ class TaskContainer implements Parcelable {
void setInvisible() {
mInfo = new TaskFragmentParentInfo(mInfo.getConfiguration(), mInfo.getDisplayId(),
- false /* visible */, mInfo.hasDirectActivity(), mInfo.getDecorSurface());
+ mInfo.getTaskId(), false /* visible */, mInfo.hasDirectActivity(),
+ mInfo.getDecorSurface());
}
boolean hasDirectActivity() {
@@ -680,34 +714,6 @@ class TaskContainer implements Parcelable {
return activityStacks;
}
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mTaskId);
- // TODO(b/289875940)
- }
-
- protected TaskContainer(Parcel in) {
- mTaskId = in.readInt();
- // TODO(b/289875940)
- }
-
- public static final Creator CREATOR = new Creator<>() {
- @Override
- public TaskContainer createFromParcel(Parcel in) {
- return new TaskContainer(in);
- }
-
- @Override
- public TaskContainer[] newArray(int size) {
- return new TaskContainer[size];
- }
- };
-
/** A wrapper class which contains the information of {@link TaskContainer} */
static final class TaskProperties {
private final int mDisplayId;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..33220c44a3b53dbe30edf865f4bb0ed927f5757e
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -0,0 +1,224 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.graphics.Matrix.MTRANS_X;
+import static android.graphics.Matrix.MTRANS_Y;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Choreographer;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
+ *
+ * The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close.
+ */
+class TaskFragmentAnimationAdapter {
+
+ /**
+ * If {@link #mOverrideLayer} is set to this value, we don't want to override the surface layer.
+ */
+ private static final int LAYER_NO_OVERRIDE = -1;
+
+ @NonNull
+ final Animation mAnimation;
+ @NonNull
+ final RemoteAnimationTarget mTarget;
+ @NonNull
+ final SurfaceControl mLeash;
+ /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+ @NonNull
+ private final Rect mWholeAnimationBounds = new Rect();
+ /**
+ * Area in absolute coordinate that should represent all the content to show for this window.
+ * This should be the end bounds for opening window, and start bounds for closing window in case
+ * the window is resizing during the open/close transition.
+ */
+ @NonNull
+ private final Rect mContentBounds = new Rect();
+ /** Offset relative to the window parent surface for {@link #mContentBounds}. */
+ @NonNull
+ private final Point mContentRelOffset = new Point();
+
+ @NonNull
+ final Transformation mTransformation = new Transformation();
+ @NonNull
+ final float[] mMatrix = new float[9];
+ @NonNull
+ final float[] mVecs = new float[4];
+ @NonNull
+ final Rect mRect = new Rect();
+ private boolean mIsFirstFrame = true;
+ private int mOverrideLayer = LAYER_NO_OVERRIDE;
+
+ TaskFragmentAnimationAdapter(@NonNull Animation animation,
+ @NonNull RemoteAnimationTarget target) {
+ this(animation, target, target.leash, target.screenSpaceBounds);
+ }
+
+ /**
+ * @param leash the surface to animate.
+ * @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't
+ * go beyond.
+ */
+ TaskFragmentAnimationAdapter(@NonNull Animation animation,
+ @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
+ @NonNull Rect wholeAnimationBounds) {
+ mAnimation = animation;
+ mTarget = target;
+ mLeash = leash;
+ mWholeAnimationBounds.set(wholeAnimationBounds);
+ if (target.mode == MODE_CLOSING) {
+ // When it is closing, we want to show the content at the start position in case the
+ // window is resizing as well. For example, when the activities is changing from split
+ // to stack, the bottom TaskFragment will be resized to fullscreen when hiding.
+ final Rect startBounds = target.startBounds;
+ final Rect endBounds = target.screenSpaceBounds;
+ mContentBounds.set(startBounds);
+ mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
+ mContentRelOffset.offset(
+ startBounds.left - endBounds.left,
+ startBounds.top - endBounds.top);
+ } else {
+ mContentBounds.set(target.screenSpaceBounds);
+ mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
+ }
+ }
+
+ /**
+ * Surface layer to be set at the first frame of the animation. We will not set the layer if it
+ * is set to {@link #LAYER_NO_OVERRIDE}.
+ */
+ final void overrideLayer(int layer) {
+ mOverrideLayer = layer;
+ }
+
+ /** Called on frame update. */
+ final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
+ if (mIsFirstFrame) {
+ t.show(mLeash);
+ if (mOverrideLayer != LAYER_NO_OVERRIDE) {
+ t.setLayer(mLeash, mOverrideLayer);
+ }
+ mIsFirstFrame = false;
+ }
+
+ // Extract the transformation to the current time.
+ mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
+ mTransformation);
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ onAnimationUpdateInner(t);
+ }
+
+ /** To be overridden by subclasses to adjust the animation surface change. */
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Update the surface position and alpha.
+ mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+
+ // Get current surface bounds in absolute coordinate.
+ // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
+ final int positionX = Math.round(mMatrix[MTRANS_X]);
+ final int positionY = Math.round(mMatrix[MTRANS_Y]);
+ final Rect cropRect = new Rect(mContentBounds);
+ cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y);
+
+ // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+ final int offsetX = cropRect.left;
+ final int offsetY = cropRect.top;
+
+ // Intersect to make sure the animation happens within the whole animation bounds.
+ if (!cropRect.intersect(mWholeAnimationBounds)) {
+ // Hide the surface when it is outside of the animation area.
+ t.setAlpha(mLeash, 0);
+ }
+
+ // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+ cropRect.offset(-offsetX, -offsetY);
+ t.setCrop(mLeash, cropRect);
+ }
+
+ /** Called after animation finished. */
+ final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
+ onAnimationUpdate(t, mAnimation.getDuration());
+ }
+
+ final long getDurationHint() {
+ return mAnimation.computeDurationHint();
+ }
+
+ /**
+ * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
+ * size change.
+ */
+ static class SnapshotAdapter extends TaskFragmentAnimationAdapter {
+
+ SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
+ // Start leash is the snapshot of the starting surface.
+ super(animation, target, target.startLeash, target.screenSpaceBounds);
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Snapshot should always be placed at the top left of the animation leash.
+ mTransformation.getMatrix().postTranslate(0, 0);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+ }
+ }
+
+ /**
+ * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
+ */
+ static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
+
+ BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
+ super(animation, target);
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ mTransformation.getMatrix().postTranslate(
+ mTarget.localBounds.left, mTarget.localBounds.top);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+
+ // The following applies an inverse scale to the clip-rect so that it crops "after" the
+ // scale instead of before.
+ mVecs[1] = mVecs[2] = 0;
+ mVecs[0] = mVecs[3] = 1;
+ mTransformation.getMatrix().mapVectors(mVecs);
+ mVecs[0] = 1.f / mVecs[0];
+ mVecs[3] = 1.f / mVecs[3];
+ final Rect clipRect = mTransformation.getClipRect();
+ mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f);
+ mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f);
+ mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f);
+ mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f);
+ t.setWindowCrop(mLeash, mRect);
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7eb9a01f57cb4ad9baa8aadfcb65caa3a6c933a
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -0,0 +1,79 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+
+import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.window.TaskFragmentOrganizer;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Controls the TaskFragment remote animations. */
+class TaskFragmentAnimationController {
+
+ private static final String TAG = "TaskFragAnimationCtrl";
+ static final boolean DEBUG = false;
+
+ private final TaskFragmentOrganizer mOrganizer;
+ private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner();
+ @VisibleForTesting
+ final RemoteAnimationDefinition mDefinition;
+ private boolean mIsRegistered;
+
+ TaskFragmentAnimationController(@NonNull TaskFragmentOrganizer organizer) {
+ mOrganizer = organizer;
+ mDefinition = new RemoteAnimationDefinition();
+ final RemoteAnimationAdapter animationAdapter =
+ new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
+ mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
+ }
+
+ void registerRemoteAnimations() {
+ if (DEBUG) {
+ Log.v(TAG, "registerRemoteAnimations");
+ }
+ if (mIsRegistered) {
+ return;
+ }
+ mOrganizer.registerRemoteAnimations(mDefinition);
+ mIsRegistered = true;
+ }
+
+ void unregisterRemoteAnimations() {
+ if (DEBUG) {
+ Log.v(TAG, "unregisterRemoteAnimations");
+ }
+ if (!mIsRegistered) {
+ return;
+ }
+ mOrganizer.unregisterRemoteAnimations();
+ mIsRegistered = false;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9b73a8290f5d7d03aa4b23c1297c0ab59b7c589
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -0,0 +1,309 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_CHANGING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+
+/** To run the TaskFragment animations. */
+class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
+
+ private static final String TAG = "TaskFragAnimationRunner";
+ 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);
+ }
+
+ @Nullable
+ private Animator mAnimator;
+
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ @NonNull RemoteAnimationTarget[] apps,
+ @NonNull RemoteAnimationTarget[] wallpapers,
+ @NonNull RemoteAnimationTarget[] nonApps,
+ @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+ if (wallpapers.length != 0 || nonApps.length != 0) {
+ throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
+ + "wallpaper or non-app windows.");
+ }
+ if (TaskFragmentAnimationController.DEBUG) {
+ Log.v(TAG, "onAnimationStart transit=" + transit);
+ }
+ mHandler.post(() -> startAnimation(transit, apps, finishedCallback));
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ mHandler.post(this::cancelAnimation);
+ }
+
+ /** Creates and starts animation. */
+ private void startAnimation(@WindowManager.TransitionOldType int transit,
+ @NonNull RemoteAnimationTarget[] targets,
+ @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+ if (mAnimator != null) {
+ Log.w(TAG, "start new animation when the previous one is not finished yet.");
+ mAnimator.cancel();
+ }
+ mAnimator = createAnimator(transit, targets, finishedCallback);
+ mAnimator.start();
+ }
+
+ /** Cancels animation. */
+ private void cancelAnimation() {
+ if (mAnimator == null) {
+ return;
+ }
+ mAnimator.cancel();
+ mAnimator = null;
+ }
+
+ /** Creates the animator given the transition type and windows. */
+ @NonNull
+ private Animator createAnimator(@WindowManager.TransitionOldType int transit,
+ @NonNull RemoteAnimationTarget[] targets,
+ @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+ final List adapters =
+ createAnimationAdapters(transit, targets);
+ long duration = 0;
+ for (TaskFragmentAnimationAdapter adapter : adapters) {
+ duration = Math.max(duration, adapter.getDurationHint());
+ }
+ final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.setDuration(duration);
+ animator.addUpdateListener((anim) -> {
+ // Update all adapters in the same transaction.
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (TaskFragmentAnimationAdapter adapter : adapters) {
+ adapter.onAnimationUpdate(t, animator.getCurrentPlayTime());
+ }
+ t.apply();
+ });
+ animator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (TaskFragmentAnimationAdapter adapter : adapters) {
+ adapter.onAnimationEnd(t);
+ }
+ t.apply();
+
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ mAnimator = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+ });
+ return animator;
+ }
+
+ /** List of {@link TaskFragmentAnimationAdapter} to handle animations on all window targets. */
+ @NonNull
+ private List createAnimationAdapters(
+ @WindowManager.TransitionOldType int transit,
+ @NonNull RemoteAnimationTarget[] targets) {
+ switch (transit) {
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
+ return createOpenAnimationAdapters(targets);
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
+ return createCloseAnimationAdapters(targets);
+ case TRANSIT_OLD_TASK_FRAGMENT_CHANGE:
+ return createChangeAnimationAdapters(targets);
+ default:
+ throw new IllegalArgumentException("Unhandled transit type=" + transit);
+ }
+ }
+
+ @NonNull
+ private List createOpenAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets) {
+ return createOpenCloseAnimationAdapters(targets, true /* isOpening */,
+ mAnimationSpec::loadOpenAnimation);
+ }
+
+ @NonNull
+ private List createCloseAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets) {
+ return createOpenCloseAnimationAdapters(targets, false /* isOpening */,
+ mAnimationSpec::loadCloseAnimation);
+ }
+
+ /**
+ * Creates {@link TaskFragmentAnimationAdapter} for OPEN and CLOSE types of transition.
+ * @param isOpening {@code true} for OPEN type, {@code false} for CLOSE type.
+ */
+ @NonNull
+ private List createOpenCloseAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets, boolean isOpening,
+ @NonNull BiFunction animationProvider) {
+ // We need to know if the target window is only a partial of the whole animation screen.
+ // If so, we will need to adjust it to make the whole animation screen looks like one.
+ final List openingTargets = new ArrayList<>();
+ final List closingTargets = new ArrayList<>();
+ final Rect openingWholeScreenBounds = new Rect();
+ final Rect closingWholeScreenBounds = new Rect();
+ for (RemoteAnimationTarget target : targets) {
+ if (target.mode != MODE_CLOSING) {
+ openingTargets.add(target);
+ openingWholeScreenBounds.union(target.screenSpaceBounds);
+ } else {
+ closingTargets.add(target);
+ closingWholeScreenBounds.union(target.screenSpaceBounds);
+ // Union the start bounds since this may be the ClosingChanging animation.
+ closingWholeScreenBounds.union(target.startBounds);
+ }
+ }
+
+ // For OPEN transition, open windows should be above close windows.
+ // For CLOSE transition, open windows should be below close windows.
+ int offsetLayer = TYPE_LAYER_OFFSET;
+ final List adapters = new ArrayList<>();
+ for (RemoteAnimationTarget target : openingTargets) {
+ final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target,
+ animationProvider, openingWholeScreenBounds);
+ if (isOpening) {
+ adapter.overrideLayer(offsetLayer++);
+ }
+ adapters.add(adapter);
+ }
+ for (RemoteAnimationTarget target : closingTargets) {
+ final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target,
+ animationProvider, closingWholeScreenBounds);
+ if (!isOpening) {
+ adapter.overrideLayer(offsetLayer++);
+ }
+ adapters.add(adapter);
+ }
+ return adapters;
+ }
+
+ @NonNull
+ private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter(
+ @NonNull RemoteAnimationTarget target,
+ @NonNull BiFunction animationProvider,
+ @NonNull Rect wholeAnimationBounds) {
+ final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
+ return new TaskFragmentAnimationAdapter(animation, target, target.leash,
+ wholeAnimationBounds);
+ }
+
+ @NonNull
+ private List createChangeAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets) {
+ if (shouldUseJumpCutForChangeAnimation(targets)) {
+ return new ArrayList<>();
+ }
+
+ final List adapters = new ArrayList<>();
+ for (RemoteAnimationTarget target : targets) {
+ if (target.mode == MODE_CHANGING) {
+ // This is the target with bounds change.
+ final Animation[] animations =
+ mAnimationSpec.createChangeBoundsChangeAnimations(target);
+ // Adapter for the starting snapshot leash.
+ adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter(
+ animations[0], target));
+ // Adapter for the ending bounds changed leash.
+ adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter(
+ animations[1], target));
+ continue;
+ }
+
+ // These are the other targets that don't have bounds change in the same transition.
+ final Animation animation;
+ if (target.hasAnimatingParent) {
+ // No-op if it will be covered by the changing parent window.
+ animation = TaskFragmentAnimationSpec.createNoopAnimation(target);
+ } else if (target.mode == MODE_CLOSING) {
+ animation = mAnimationSpec.createChangeBoundsCloseAnimation(target);
+ } else {
+ animation = mAnimationSpec.createChangeBoundsOpenAnimation(target);
+ }
+ adapters.add(new TaskFragmentAnimationAdapter(animation, target));
+ }
+ return adapters;
+ }
+
+ /**
+ * Whether we should use jump cut for the change transition.
+ * This normally happens when opening a new secondary with the existing primary using a
+ * different split layout. This can be complicated, like from horizontal to vertical split with
+ * new split pairs.
+ * Uses a jump cut animation to simplify.
+ */
+ private boolean shouldUseJumpCutForChangeAnimation(@NonNull RemoteAnimationTarget[] targets) {
+ boolean hasOpeningWindow = false;
+ boolean hasClosingWindow = false;
+ for (RemoteAnimationTarget target : targets) {
+ if (target.hasAnimatingParent) {
+ continue;
+ }
+ hasOpeningWindow |= target.mode == MODE_OPENING;
+ hasClosingWindow |= target.mode == MODE_CLOSING;
+ }
+ return hasOpeningWindow && hasClosingWindow;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f866c3b99c987b1d1cebfdd2500adfc961a78e2
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -0,0 +1,267 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.provider.Settings;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.TransitionAnimation;
+
+/** Animation spec for TaskFragment transition. */
+// TODO(b/206557124): provide an easier way to customize animation
+class TaskFragmentAnimationSpec {
+
+ private static final String TAG = "TaskFragAnimationSpec";
+ private static final int CHANGE_ANIMATION_DURATION = 517;
+ private static final int CHANGE_ANIMATION_FADE_DURATION = 80;
+ private static final int CHANGE_ANIMATION_FADE_OFFSET = 30;
+
+ private final Context mContext;
+ private final TransitionAnimation mTransitionAnimation;
+ private final Interpolator mFastOutExtraSlowInInterpolator;
+ private final LinearInterpolator mLinearInterpolator;
+ private float mTransitionAnimationScaleSetting;
+
+ TaskFragmentAnimationSpec(@NonNull Handler handler) {
+ mContext = ActivityThread.currentActivityThread().getApplication();
+ mTransitionAnimation = new TransitionAnimation(mContext, false /* debug */, TAG);
+ // Initialize the AttributeCache for the TransitionAnimation.
+ AttributeCache.init(mContext);
+ mFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.fast_out_extra_slow_in);
+ mLinearInterpolator = new LinearInterpolator();
+
+ // The transition animation should be adjusted based on the developer option.
+ final ContentResolver resolver = mContext.getContentResolver();
+ mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
+ new SettingsObserver(handler));
+ }
+
+ /** For target that doesn't need to be animated. */
+ @NonNull
+ static Animation createNoopAnimation(@NonNull RemoteAnimationTarget target) {
+ // Noop but just keep the target showing/hiding.
+ final float alpha = target.mode == MODE_CLOSING ? 0f : 1f;
+ return new AlphaAnimation(alpha, alpha);
+ }
+
+ /** Animation for target that is opening in a change transition. */
+ @NonNull
+ Animation createChangeBoundsOpenAnimation(@NonNull RemoteAnimationTarget target) {
+ final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+ final Rect bounds = target.screenSpaceBounds;
+ final int startLeft;
+ final int startTop;
+ if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+ // The window will be animated in from left or right depending on its position.
+ startTop = 0;
+ startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
+ } else {
+ // The window will be animated in from top or bottom depending on its position.
+ startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
+ startLeft = 0;
+ }
+
+ // The position should be 0-based as we will post translate in
+ // TaskFragmentAnimationAdapter#onAnimationUpdate
+ final Animation animation = new TranslateAnimation(startLeft, 0, startTop, 0);
+ animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+ animation.setDuration(CHANGE_ANIMATION_DURATION);
+ animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ /** Animation for target that is closing in a change transition. */
+ @NonNull
+ Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
+ final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+ // Use startBounds if the window is closing in case it may also resize.
+ final Rect bounds = target.startBounds;
+ final int endTop;
+ final int endLeft;
+ if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+ // The window will be animated out to left or right depending on its position.
+ endTop = 0;
+ endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
+ } else {
+ // The window will be animated out to top or bottom depending on its position.
+ endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
+ endLeft = 0;
+ }
+
+ // The position should be 0-based as we will post translate in
+ // TaskFragmentAnimationAdapter#onAnimationUpdate
+ final Animation animation = new TranslateAnimation(0, endLeft, 0, endTop);
+ animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+ animation.setDuration(CHANGE_ANIMATION_DURATION);
+ animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ /**
+ * Animation for target that is changing (bounds change) in a change transition.
+ * @return the return array always has two elements. The first one is for the start leash, and
+ * the second one is for the end leash.
+ */
+ @NonNull
+ Animation[] createChangeBoundsChangeAnimations(@NonNull RemoteAnimationTarget target) {
+ // Both start bounds and end bounds are in screen coordinates. We will post translate
+ // to the local coordinates in TaskFragmentAnimationAdapter#onAnimationUpdate
+ final Rect startBounds = target.startBounds;
+ final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+ final Rect endBounds = target.screenSpaceBounds;
+ float scaleX = ((float) startBounds.width()) / endBounds.width();
+ float scaleY = ((float) startBounds.height()) / endBounds.height();
+ // Start leash is a child of the end leash. Reverse the scale so that the start leash won't
+ // be scaled up with its parent.
+ float startScaleX = 1.f / scaleX;
+ float startScaleY = 1.f / scaleY;
+
+ // The start leash will be fade out.
+ final AnimationSet startSet = new AnimationSet(false /* shareInterpolator */);
+ final Animation startAlpha = new AlphaAnimation(1f, 0f);
+ startAlpha.setInterpolator(mLinearInterpolator);
+ startAlpha.setDuration(CHANGE_ANIMATION_FADE_DURATION);
+ startAlpha.setStartOffset(CHANGE_ANIMATION_FADE_OFFSET);
+ startSet.addAnimation(startAlpha);
+ final Animation startScale = new ScaleAnimation(startScaleX, startScaleX, startScaleY,
+ startScaleY);
+ startScale.setInterpolator(mFastOutExtraSlowInInterpolator);
+ startScale.setDuration(CHANGE_ANIMATION_DURATION);
+ startSet.addAnimation(startScale);
+ startSet.initialize(startBounds.width(), startBounds.height(), endBounds.width(),
+ endBounds.height());
+ startSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+ // The end leash will be moved into the end position while scaling.
+ final AnimationSet endSet = new AnimationSet(true /* shareInterpolator */);
+ endSet.setInterpolator(mFastOutExtraSlowInInterpolator);
+ final Animation endScale = new ScaleAnimation(scaleX, 1, scaleY, 1);
+ endScale.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(endScale);
+ // The position should be 0-based as we will post translate in
+ // TaskFragmentAnimationAdapter#onAnimationUpdate
+ final Animation endTranslate = new TranslateAnimation(startBounds.left - endBounds.left, 0,
+ startBounds.top - endBounds.top, 0);
+ endTranslate.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(endTranslate);
+ // The end leash is resizing, we should update the window crop based on the clip rect.
+ final Rect startClip = new Rect(startBounds);
+ final Rect endClip = new Rect(endBounds);
+ startClip.offsetTo(0, 0);
+ endClip.offsetTo(0, 0);
+ final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
+ clipAnim.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(clipAnim);
+ endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(),
+ parentBounds.height());
+ endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+ return new Animation[]{startSet, endSet};
+ }
+
+ @NonNull
+ Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
+ @NonNull Rect wholeAnimationBounds) {
+ final boolean isEnter = target.mode != MODE_CLOSING;
+ final Animation animation;
+ // Background color on TaskDisplayArea has already been set earlier in
+ // WindowContainer#getAnimationAdapter.
+ if (target.showBackdrop) {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
+ : com.android.internal.R.anim.task_fragment_clear_top_open_exit);
+ } else {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_open_enter
+ : com.android.internal.R.anim.task_fragment_open_exit);
+ }
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are opening at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are launching together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
+ wholeAnimationBounds.width(), wholeAnimationBounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ @NonNull
+ Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
+ @NonNull Rect wholeAnimationBounds) {
+ final boolean isEnter = target.mode != MODE_CLOSING;
+ final Animation animation;
+ if (target.showBackdrop) {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
+ : com.android.internal.R.anim.task_fragment_clear_top_close_exit);
+ } else {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_close_enter
+ : com.android.internal.R.anim.task_fragment_close_exit);
+ }
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are closing at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are finishing together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
+ wholeAnimationBounds.width(), wholeAnimationBounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ private float getTransitionAnimationScaleSetting() {
+ return WindowManager.fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat(
+ R.dimen.config_appTransitionAnimationDurationScaleDefault)));
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ SettingsObserver(@NonNull Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
+ }
+ }
+}
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 dc6506b070af0fa3bae8dfc925ff5bbc871e791f..dc1d983997c636acfbd0d2057d16e01bd97a3006 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -53,14 +53,12 @@ import java.util.Objects;
class TaskFragmentContainer {
private static final int APPEAR_EMPTY_TIMEOUT_MS = 3000;
+ /** Parcelable data of this TaskFragmentContainer. */
@NonNull
- private final SplitController mController;
+ private final ParcelableTaskFragmentContainerData mParcelableData;
- /**
- * Client-created token that uniquely identifies the task fragment container instance.
- */
@NonNull
- private final IBinder mToken;
+ private final SplitController mController;
/** Parent leaf Task. */
@NonNull
@@ -103,9 +101,6 @@ class TaskFragmentContainer {
*/
private final List mActivitiesToFinishOnExit = new ArrayList<>();
- @Nullable
- private final String mOverlayTag;
-
/**
* The launch options that was used to create this container. Must not {@link Bundle#isEmpty()}
* for {@link #isOverlay()} container.
@@ -113,29 +108,13 @@ class TaskFragmentContainer {
@NonNull
private final Bundle mLaunchOptions = new Bundle();
- /**
- * The associated {@link Activity#getActivityToken()} of the overlay container.
- * Must be {@code null} for non-overlay container.
- *
- * If an overlay container is associated with an activity, this overlay container will be
- * dismissed when the associated activity is destroyed. If the overlay container is visible,
- * activity will be launched on top of the overlay container and expanded to fill the parent
- * container.
- */
- @Nullable
- private final IBinder mAssociatedActivityToken;
-
/** Indicates whether the container was cleaned up after the last activity was removed. */
private boolean mIsFinished;
- /**
- * Bounds that were requested last via {@link android.window.WindowContainerTransaction}.
- */
- private final Rect mLastRequestedBounds = new Rect();
-
/**
* Windowing mode that was requested last via {@link android.window.WindowContainerTransaction}.
*/
+ // TODO(b/289875940): review this and other field that might need to be moved in the base class.
@WindowingMode
private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED;
@@ -208,17 +187,17 @@ class TaskFragmentContainer {
@NonNull SplitController controller,
@Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
@Nullable Bundle launchOptions, @Nullable Activity associatedActivity) {
+ mParcelableData = new ParcelableTaskFragmentContainerData(
+ new Binder("TaskFragmentContainer"), overlayTag,
+ associatedActivity != null ? associatedActivity.getActivityToken() : null);
+
if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
|| (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
throw new IllegalArgumentException(
"One and only one of pending activity and intent must be non-null");
}
mController = controller;
- mToken = new Binder("TaskFragmentContainer");
mTaskContainer = taskContainer;
- mOverlayTag = overlayTag;
- mAssociatedActivityToken = associatedActivity != null
- ? associatedActivity.getActivityToken() : null;
if (launchOptions != null) {
mLaunchOptions.putAll(launchOptions);
@@ -259,18 +238,26 @@ class TaskFragmentContainer {
if (overlayTag != null && pendingAppearedIntent != null
&& associatedActivity != null && !associatedActivity.isFinishing()) {
final IBinder associatedActivityToken = associatedActivity.getActivityToken();
- final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken,
- launchOptions, pendingAppearedIntent);
+ final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(
+ mParcelableData.mToken, launchOptions, pendingAppearedIntent);
mController.mOverlayRestoreParams.put(associatedActivityToken, params);
}
}
+ /** This is only used when restoring it from a {@link ParcelableTaskFragmentContainerData}. */
+ TaskFragmentContainer(@NonNull ParcelableTaskFragmentContainerData data,
+ @NonNull SplitController splitController, @NonNull TaskContainer taskContainer) {
+ mParcelableData = data;
+ mController = splitController;
+ mTaskContainer = taskContainer;
+ }
+
/**
* Returns the client-created token that uniquely identifies this container.
*/
@NonNull
IBinder getTaskFragmentToken() {
- return mToken;
+ return mParcelableData.mToken;
}
/** List of non-finishing activities that belong to this container and live in this process. */
@@ -389,7 +376,8 @@ class TaskFragmentContainer {
return null;
}
return new ActivityStack(activities, isEmpty(),
- ActivityStack.Token.createFromBinder(mToken), mOverlayTag);
+ ActivityStack.Token.createFromBinder(mParcelableData.mToken),
+ mParcelableData.mOverlayTag);
}
/** Adds the activity that will be reparented to this container. */
@@ -413,7 +401,7 @@ class TaskFragmentContainer {
final ActivityThread.ActivityClientRecord record = ActivityThread
.currentActivityThread().getActivityClient(activityToken);
if (record != null) {
- record.mTaskFragmentToken = mToken;
+ record.mTaskFragmentToken = mParcelableData.mToken;
}
}
@@ -469,7 +457,7 @@ class TaskFragmentContainer {
if (!isOverlayWithActivityAssociation()) {
return;
}
- if (mAssociatedActivityToken == activityToken) {
+ if (mParcelableData.mAssociatedActivityToken == activityToken) {
// If the associated activity is destroyed, also finish this overlay container.
mController.mPresenter.cleanupContainer(wct, this, false /* shouldFinishDependent */);
}
@@ -776,8 +764,8 @@ class TaskFragmentContainer {
* @see WindowContainerTransaction#setRelativeBounds
*/
boolean areLastRequestedBoundsEqual(@Nullable Rect relBounds) {
- return (relBounds == null && mLastRequestedBounds.isEmpty())
- || mLastRequestedBounds.equals(relBounds);
+ return (relBounds == null && mParcelableData.mLastRequestedBounds.isEmpty())
+ || mParcelableData.mLastRequestedBounds.equals(relBounds);
}
/**
@@ -787,14 +775,14 @@ class TaskFragmentContainer {
*/
void setLastRequestedBounds(@Nullable Rect relBounds) {
if (relBounds == null) {
- mLastRequestedBounds.setEmpty();
+ mParcelableData.mLastRequestedBounds.setEmpty();
} else {
- mLastRequestedBounds.set(relBounds);
+ mParcelableData.mLastRequestedBounds.set(relBounds);
}
}
@NonNull Rect getLastRequestedBounds() {
- return mLastRequestedBounds;
+ return mParcelableData.mLastRequestedBounds;
}
/**
@@ -965,6 +953,16 @@ class TaskFragmentContainer {
return mTaskContainer.getTaskId();
}
+ @NonNull
+ IBinder getToken() {
+ return mParcelableData.mToken;
+ }
+
+ @NonNull
+ ParcelableTaskFragmentContainerData getParcelableData() {
+ return mParcelableData;
+ }
+
/** Gets the parent Task. */
@NonNull
TaskContainer getTaskContainer() {
@@ -1011,7 +1009,7 @@ class TaskFragmentContainer {
/** Returns whether this taskFragment container is an overlay container. */
boolean isOverlay() {
- return mOverlayTag != null;
+ return mParcelableData.mOverlayTag != null;
}
/**
@@ -1020,7 +1018,7 @@ class TaskFragmentContainer {
*/
@Nullable
String getOverlayTag() {
- return mOverlayTag;
+ return mParcelableData.mOverlayTag;
}
/**
@@ -1045,7 +1043,7 @@ class TaskFragmentContainer {
*/
@Nullable
IBinder getAssociatedActivityToken() {
- return mAssociatedActivityToken;
+ return mParcelableData.mAssociatedActivityToken;
}
/**
@@ -1053,11 +1051,11 @@ class TaskFragmentContainer {
* a non-fill-parent overlay without activity association.
*/
boolean isAlwaysOnTopOverlay() {
- return isOverlay() && mAssociatedActivityToken == null;
+ return isOverlay() && mParcelableData.mAssociatedActivityToken == null;
}
boolean isOverlayWithActivityAssociation() {
- return isOverlay() && mAssociatedActivityToken != null;
+ return isOverlay() && mParcelableData.mAssociatedActivityToken != null;
}
@Override
@@ -1074,13 +1072,13 @@ class TaskFragmentContainer {
private String toString(boolean includeContainersToFinishOnExit) {
return "TaskFragmentContainer{"
+ " parentTaskId=" + getTaskId()
- + " token=" + mToken
+ + " token=" + mParcelableData.mToken
+ " topNonFinishingActivity=" + getTopNonFinishingActivity()
+ " runningActivityCount=" + getRunningActivityCount()
+ " isFinished=" + mIsFinished
- + " overlayTag=" + mOverlayTag
- + " associatedActivityToken=" + mAssociatedActivityToken
- + " lastRequestedBounds=" + mLastRequestedBounds
+ + " overlayTag=" + mParcelableData.mOverlayTag
+ + " associatedActivityToken=" + mParcelableData.mAssociatedActivityToken
+ + " lastRequestedBounds=" + mParcelableData.mLastRequestedBounds
+ " pendingAppearedActivities=" + mPendingAppearedActivities
+ (includeContainersToFinishOnExit ? " containersToFinishOnExit="
+ containersToFinishOnExitToString() : "")
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 8911d18b9b9726b8144f1a9284cabdaea8234a21..ac004c3015982c39fd3b28784be354ec69bb44ec 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -23,6 +23,8 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTest
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.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -83,6 +85,24 @@ public class JetpackTaskFragmentOrganizerTest {
doReturn(mHandler).when(mSplitController).getHandler();
}
+ @Test
+ public void testUnregisterOrganizer() {
+ mOrganizer.overrideSplitAnimation();
+ mOrganizer.unregisterOrganizer();
+
+ verify(mOrganizer).unregisterRemoteAnimations();
+ }
+
+ @Test
+ public void testOverrideSplitAnimation() {
+ assertNull(mOrganizer.mAnimationController);
+
+ mOrganizer.overrideSplitAnimation();
+
+ assertNotNull(mOrganizer.mAnimationController);
+ verify(mOrganizer).registerRemoteAnimations(mOrganizer.mAnimationController.mDefinition);
+ }
+
@Test
public void testExpandTaskFragment() {
final TaskContainer taskContainer = createTestTaskContainer();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 90eeb583d0706a0a6459155a8209eeaea1d716a0..5b97e7e2ca7155ea11802801372c50b7c40266ca 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -549,7 +549,7 @@ public class OverlayPresentationTest {
assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
@@ -618,7 +618,8 @@ public class OverlayPresentationTest {
final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
- true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+ TASK_ID, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */);
parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10);
mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
@@ -642,7 +643,8 @@ public class OverlayPresentationTest {
final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
- true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+ TASK_ID, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */);
mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index d852204b88a858551112bbc954ac8344d9184fa3..05124121fe7ba266fe97a4937543ecd561ac0c07 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1164,7 +1164,7 @@ public class SplitControllerTest {
public void testOnTransactionReady_taskFragmentParentInfoChanged() {
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */);
transaction.addChange(new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
@@ -1625,7 +1625,7 @@ public class SplitControllerTest {
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
final Configuration configuration = new Configuration();
final TaskFragmentParentInfo originalInfo = new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */);
mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
TASK_ID, originalInfo);
@@ -1634,7 +1634,7 @@ public class SplitControllerTest {
// Making a public configuration change while the Task is invisible.
configuration.densityDpi += 100;
final TaskFragmentParentInfo invisibleInfo = new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, false /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, false /* visible */, false /* hasDirectActivity */,
null /* decorSurface */);
mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
TASK_ID, invisibleInfo);
@@ -1646,7 +1646,7 @@ public class SplitControllerTest {
// Updates when Task to become visible
final TaskFragmentParentInfo visibleInfo = new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */);
mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
TASK_ID, visibleInfo);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 284723279b80ee7ef2f76dda3301519e0fb7c98f..97f4d0736312680be99b2c9c41bf1dd3a4627745 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static org.junit.Assert.assertEquals;
@@ -82,7 +83,7 @@ public class TaskContainerTest {
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
@@ -90,7 +91,7 @@ public class TaskContainerTest {
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
assertEquals(WINDOWING_MODE_FREEFORM,
@@ -111,14 +112,14 @@ public class TaskContainerTest {
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
assertFalse(taskContainer.isInPictureInPicture());
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
null /* decorSurface */));
assertTrue(taskContainer.isInPictureInPicture());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a1e9f08585f661867c831fcd541600627b2bec69
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.mockito.Mockito.never;
+
+import android.platform.test.annotations.Presubmit;
+import android.window.TaskFragmentOrganizer;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Test class for {@link TaskFragmentAnimationController}.
+ *
+ * Build/Install/Run:
+ * atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TaskFragmentAnimationControllerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock
+ private TaskFragmentOrganizer mOrganizer;
+ private TaskFragmentAnimationController mAnimationController;
+
+ @Before
+ public void setup() {
+ mAnimationController = new TaskFragmentAnimationController(mOrganizer);
+ }
+
+ @Test
+ public void testRegisterRemoteAnimations() {
+ mAnimationController.registerRemoteAnimations();
+
+ verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
+
+ mAnimationController.registerRemoteAnimations();
+
+ // No extra call if it has been registered.
+ verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
+ }
+
+ @Test
+ public void testUnregisterRemoteAnimations() {
+ mAnimationController.unregisterRemoteAnimations();
+
+ // No call if it is not registered.
+ verify(mOrganizer, never()).unregisterRemoteAnimations();
+
+ mAnimationController.registerRemoteAnimations();
+ mAnimationController.unregisterRemoteAnimations();
+
+ verify(mOrganizer).unregisterRemoteAnimations();
+
+ mAnimationController.unregisterRemoteAnimations();
+
+ // No extra call if it has been unregistered.
+ verify(mOrganizer).unregisterRemoteAnimations();
+ }
+}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index b338a2ae2b79bff45a1f5aebb2a44e21c7328245..a79bc97c440c329c1d509c69f701d913af6bfcb3 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -39,17 +39,6 @@ filegroup {
path: "src",
}
-// Sources that have no dependencies that can be used directly downstream of this library
-// TODO(b/322791067): move these sources to WindowManager-Shell-shared
-filegroup {
- name: "wm_shell_util-sources",
- srcs: [
- "src/com/android/wm/shell/common/bubbles/*.kt",
- "src/com/android/wm/shell/common/bubbles/*.java",
- ],
- path: "src",
-}
-
// Aidls which can be used directly downstream of this library
filegroup {
name: "wm_shell-aidls",
@@ -184,9 +173,11 @@ java_library {
":wm_shell-shared-aidls",
],
static_libs: [
+ "androidx.core_core-animation",
"androidx.dynamicanimation_dynamicanimation",
"jsr330",
],
+ kotlincflags: ["-Xjvm-default=all"],
}
java_library {
@@ -212,7 +203,6 @@ android_library {
],
static_libs: [
"androidx.appcompat_appcompat",
- "androidx.core_core-animation",
"androidx.core_core-ktx",
"androidx.arch.core_core-runtime",
"androidx.datastore_datastore",
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index bbbc23e8b922f78e6d7f75b03037768301aaf14f..3b739c3d581706b8c55f585de656e2a8ca0a80eb 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -23,6 +23,7 @@
+
()
+ private val windowManager = context.getSystemService(WindowManager::class.java)
+
+ private lateinit var mainExecutor: TestExecutor
+ private lateinit var bgExecutor: TestExecutor
+
+ private lateinit var expandedViewManager: BubbleExpandedViewManager
+ private lateinit var positioner: BubblePositioner
+ private lateinit var bubbleTaskView: BubbleTaskView
+
+ private lateinit var bubbleExpandedView: BubbleBarExpandedView
+ private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null
+ private var regionSamplingProvider: TestRegionSamplingProvider? = null
+
+ @Before
+ fun setUp() {
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ mainExecutor = TestExecutor()
+ bgExecutor = TestExecutor()
+ positioner = BubblePositioner(context, windowManager)
+ positioner.setShowingInBubbleBar(true)
+ val deviceConfig =
+ DeviceConfig(
+ windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+ isLargeScreen = true,
+ isSmallTablet = false,
+ isLandscape = true,
+ isRtl = false,
+ insets = Insets.of(10, 20, 30, 40)
+ )
+ positioner.update(deviceConfig)
+
+ expandedViewManager = createExpandedViewManager()
+ bubbleTaskView = FakeBubbleTaskViewFactory().create()
+
+ val inflater = LayoutInflater.from(context)
+
+ regionSamplingProvider = TestRegionSamplingProvider()
+
+ bubbleExpandedView = (inflater.inflate(
+ R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */
+ ) as BubbleBarExpandedView)
+ bubbleExpandedView.initialize(
+ expandedViewManager,
+ positioner,
+ false /* isOverflow */,
+ bubbleTaskView,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingProvider
+ )
+
+ getInstrumentation().runOnMainSync(Runnable {
+ bubbleExpandedView.onAttachedToWindow()
+ // Helper should be created once attached to window
+ testableRegionSamplingHelper = regionSamplingProvider!!.helper
+ })
+ }
+
+ @After
+ fun tearDown() {
+ testableRegionSamplingHelper?.stopAndDestroy()
+ }
+
+ @Test
+ fun testCreateSamplingHelper_onAttach() {
+ assertThat(testableRegionSamplingHelper).isNotNull()
+ }
+
+ @Test
+ fun testDestroySamplingHelper_onDetach() {
+ bubbleExpandedView.onDetachedFromWindow()
+ assertThat(testableRegionSamplingHelper!!.isDestroyed).isTrue()
+ }
+
+ @Test
+ fun testStopSampling_onDragStart() {
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+
+ bubbleExpandedView.setDragging(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ @Test
+ fun testStartSampling_onDragEnd() {
+ bubbleExpandedView.setDragging(true)
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+
+ bubbleExpandedView.setDragging(false)
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testStartSampling_onContentVisible() {
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.setWindowVisible).isTrue()
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testStopSampling_onContentInvisible() {
+ bubbleExpandedView.setContentVisibility(false)
+
+ assertThat(testableRegionSamplingHelper!!.setWindowInvisible).isTrue()
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ @Test
+ fun testSampling_startStopAnimating_visible() {
+ bubbleExpandedView.isAnimating = true
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+
+ bubbleExpandedView.isAnimating = false
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testSampling_startStopAnimating_invisible() {
+ bubbleExpandedView.isAnimating = true
+ bubbleExpandedView.setContentVisibility(false)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ testableRegionSamplingHelper!!.reset()
+
+ bubbleExpandedView.isAnimating = false
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
+ override fun create(): BubbleTaskView {
+ val taskViewTaskController = mock()
+ val taskView = TaskView(context, taskViewTaskController)
+ val taskInfo = mock()
+ whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+ return BubbleTaskView(taskView, mainExecutor)
+ }
+ }
+
+ private inner class TestRegionSamplingProvider : RegionSamplingProvider {
+
+ lateinit var helper: TestableRegionSamplingHelper
+
+ override fun createHelper(
+ sampledView: View?,
+ callback: RegionSamplingHelper.SamplingCallback?,
+ backgroundExecutor: Executor?,
+ mainExecutor: Executor?
+ ): RegionSamplingHelper {
+ helper = TestableRegionSamplingHelper(sampledView, callback, backgroundExecutor,
+ mainExecutor)
+ return helper
+ }
+ }
+
+ private inner class TestableRegionSamplingHelper(
+ sampledView: View?,
+ samplingCallback: SamplingCallback?,
+ backgroundExecutor: Executor?,
+ mainExecutor: Executor?
+ ) : RegionSamplingHelper(sampledView, samplingCallback, backgroundExecutor, mainExecutor) {
+
+ var isStarted = false
+ var isStopped = false
+ var isDestroyed = false
+ var setWindowVisible = false
+ var setWindowInvisible = false
+
+ override fun start(initialSamplingBounds: Rect) {
+ super.start(initialSamplingBounds)
+ isStarted = true
+ }
+
+ override fun stop() {
+ super.stop()
+ isStopped = true
+ }
+
+ override fun stopAndDestroy() {
+ super.stopAndDestroy()
+ isDestroyed = true
+ }
+
+ override fun setWindowVisible(visible: Boolean) {
+ super.setWindowVisible(visible)
+ if (visible) {
+ setWindowVisible = true
+ } else {
+ setWindowInvisible = true
+ }
+ }
+
+ fun reset() {
+ isStarted = false
+ isStopped = false
+ isDestroyed = false
+ setWindowVisible = false
+ setWindowInvisible = false
+ }
+ }
+
+ private fun createExpandedViewManager(): BubbleExpandedViewManager {
+ return object : BubbleExpandedViewManager {
+ override val overflowBubbles: List
+ get() = Collections.emptyList()
+
+ override fun setOverflowListener(listener: BubbleData.Listener) {
+ }
+
+ override fun collapseStack() {
+ }
+
+ override fun updateWindowFlagsForBackpress(intercept: Boolean) {
+ }
+
+ override fun promoteBubbleFromOverflow(bubble: Bubble) {
+ }
+
+ override fun removeBubble(key: String, reason: Int) {
+ }
+
+ override fun dismissBubble(bubble: Bubble, reason: Int) {
+ }
+
+ override fun setAppBubbleTaskId(key: String, taskId: Int) {
+ }
+
+ override fun isStackExpanded(): Boolean {
+ return true
+ }
+
+ override fun isShowingAsBubbleBar(): Boolean {
+ return true
+ }
+
+ override fun hideCurrentInputMethod() {
+ }
+
+ override fun updateBubbleBarLocation(location: BubbleBarLocation) {
+ }
+ }
+ }
+
+ private class TestExecutor : ShellExecutor {
+
+ private val runnables: MutableList = mutableListOf()
+
+ override fun execute(runnable: Runnable) {
+ runnables.add(runnable)
+ }
+
+ override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
+ execute(runnable)
+ }
+
+ override fun removeCallbacks(runnable: Runnable?) {}
+
+ override fun hasCallback(runnable: Runnable?): Boolean = false
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index 935d12916f566ade7e9914dbe2eba61bd349a426..ecb2b25a02f1583c87fd4f1fcff5456994814392 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -31,12 +31,12 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.DeviceConfig
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index a0a06f1b37216af4a6e9ae3ef509ae8fa85aebc0..806d026a7e7cf9033ebfa85ab1005f33e35dbecb 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index b489a5c1acd0782093c19287eaf19ce5bdac59f5..7fa586c626be9e62b3a38c0a6d2701e69c55797a 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 08d0dd3d9789f5a93135cfa9b87748a79c869f18..0ed5a72af44c9d65aea55b642ac203b04e559906 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -73,6 +73,8 @@
"vou %1$s in"
"%1$s-instellings"
"Maak borrel toe"
+
+
"Moenie dat gesprek \'n borrel word nie"
"Klets met borrels"
"Nuwe gesprekke verskyn as swerwende ikone, of borrels Tik op borrel om dit oop te maak. Sleep om dit te skuif."
@@ -127,6 +129,5 @@
"Maak kieslys oop"
"Maksimeer skerm"
"Gryp skerm vas"
-
-
+ "Hierdie app se grootte kan nie verander word nie"
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 9efbcb5729ef197b1a1ebe522693e5ab4b9f52a5..c4d9158caf46763497b8895fe8d34e5b057d6180 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -73,6 +73,8 @@
"%1$sን ሰብስብ"
"የ%1$s ቅንብሮች"
"አረፋን አሰናብት"
+
+
"ውይይቶችን በአረፋ አታሳይ"
"አረፋዎችን በመጠቀም ይወያዩ"
"አዲስ ውይይቶች እንደ ተንሳፋፊ አዶዎች ወይም አረፋዎች ሆነው ይታያሉ። አረፋን ለመክፈት መታ ያድርጉ። ለመውሰድ ይጎትቱት።"
@@ -127,6 +129,5 @@
"ምናሌን ክፈት"
"የማያ ገጹ መጠን አሳድግ"
"ማያ ገጹን አሳድግ"
-
-
+ "ይህ መተግበሪያ መጠኑ ሊቀየር አይችልም"
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 433d99ade09e9a62b7c69c9ce48dadd43843b6ff..ced9ee95a1781c2f6b6b5eafc00d256274c3effb 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -73,6 +73,8 @@
"تصغير %1$s"
"إعدادات %1$s"
"إغلاق فقاعة المحادثة"
+
+
"عدم عرض المحادثة كفقاعة محادثة"
"الدردشة باستخدام فقاعات المحادثات"
"تظهر المحادثات الجديدة كرموز عائمة أو كفقاعات. انقر لفتح فقاعة المحادثة، واسحبها لتحريكها."
@@ -127,6 +129,5 @@
"فتح القائمة"
"تكبير الشاشة إلى أقصى حدّ"
"التقاط صورة للشاشة"
-
-
+ "لا يمكن تغيير حجم نافذة هذا التطبيق"
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 47e78f5991f57e0366d9f3a90a55f86fd85f793b..273d043c45327939429372976d6d2d9700a64500 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -73,6 +73,8 @@
"%1$s সংকোচন কৰক"
"%1$s ছেটিং"
"বাবল অগ্ৰাহ্য কৰক"
+
+
"বাৰ্তালাপ বাবল নকৰিব"
"Bubbles ব্যৱহাৰ কৰি চাট কৰক"
"নতুন বাৰ্তালাপ উপঙি থকা চিহ্নসমূহ অথবা bubbles হিচাপে প্ৰদর্শিত হয়। Bubbles খুলিবলৈ টিপক। এইটো স্থানান্তৰ কৰিবলৈ টানি নিয়ক।"
@@ -127,6 +129,5 @@
"মেনু খোলক"
"স্ক্ৰীন মেক্সিমাইজ কৰক"
"স্ক্ৰীন স্নেপ কৰক"
-
-
+ "এই এপ্টোৰ আকাৰ সলনি কৰিব নোৱাৰি"
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 380a34adba08f7fe912e77c7332beb7f3a50713e..81bb544217d53fd0d7d833dc431709110a242ccd 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -73,6 +73,8 @@
"yığcamlaşdırın: %1$s"
"%1$s ayarları"
"Yumrucuğu ləğv edin"
+
+
"Söhbəti yumrucuqda göstərmə"
"Yumrucuqlardan istifadə edərək söhbət edin"
"Yeni söhbətlər üzən nişanlar və ya yumrucuqlar kimi görünür. Yumrucuğu açmaq üçün toxunun. Hərəkət etdirmək üçün sürüşdürün."
@@ -127,6 +129,5 @@
"Menyunu açın"
"Ekranı maksimum böyüdün"
"Ekranı çəkin"
-
-
+ "Bu tətbiqin ölçüsünü dəyişmək olmur"
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 b09c7b1cd4c089194b217627fa1e2cabfb841079..898b8445d0e142517e71ed7bbcee4ea1c607c529 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -73,6 +73,8 @@
"skupite oblačić %1$s"
"Podešavanja za %1$s"
"Odbaci oblačić"
+
+
"Ne koristi oblačiće za konverzaciju"
"Ćaskajte u oblačićima"
"Nove konverzacije se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da biste otvorili oblačić. Prevucite da biste ga premestili."
@@ -127,6 +129,5 @@
"Otvorite meni"
"Povećaj ekran"
"Uklopi ekran"
-
-
+ "Veličina ove aplikacije ne može da se promeni"
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index f5945b1237364df4ca55cb8fc059c0fc71a7f2fe..c3407296a007c21f9fa97942b5acf68695573ce5 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -73,6 +73,8 @@
"%1$s: згарнуць"
"Налады \"%1$s\""
"Адхіліць апавяшчэнне"
+
+
"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"
"Усплывальныя чаты"
"Новыя размовы будуць паказвацца як рухомыя значкі ці ўсплывальныя чаты. Націсніце, каб адкрыць усплывальны чат. Перацягніце яго, каб перамясціць."
@@ -127,6 +129,5 @@
"Адкрыць меню"
"Разгарнуць на ўвесь экран"
"Размясціць на палавіне экрана"
-
-
+ "Немагчыма змяніць памер праграмы"
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 4d1208b8d4c9ef0d486693562c73bd3a5d1f6b44..076a815a25ae64f4a011184060478005d68ab738 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -73,6 +73,8 @@
"свиване на %1$s"
"Настройки за %1$s"
"Отхвърляне на балончетата"
+
+
"Без балончета за разговора"
"Чат с балончета"
"Новите разговори се показват като плаващи икони, или балончета. Докоснете балонче, за да го отворите, или го плъзнете, за да го преместите."
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 6b1f3850e00fa7e4e8a77cb238daf82ea7098afe..b0d66967bbb4e63e47c80ab11f837e141b5a7e72 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -73,6 +73,8 @@
"%1$s আড়াল করুন"
"%1$s সেটিংস"
"বাবল খারিজ করুন"
+
+
"কথোপকথন বাবল হিসেবে দেখাবে না"
"বাবল ব্যবহার করে চ্যাট করুন"
"নতুন কথোপকথন ভেসে থাকা আইকন বা বাবল হিসেবে দেখানো হয়। বাবল খুলতে ট্যাপ করুন। সেটি সরাতে ধরে টেনে আনুন।"
@@ -127,6 +129,5 @@
"মেনু খুলুন"
"স্ক্রিন বড় করুন"
"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"
-
-
+ "এই অ্যাপ ছোট বড় করা যাবে না"
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 6bc5274aecea848b91981f5ed32ca10240d58e5e..0196e5e93a17d59ed7e0fe19d1cfc7dfc2ea06a9 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -73,6 +73,8 @@
"sužavanje oblačića %1$s"
"Postavke aplikacije %1$s"
"Odbaci oblačić"
+
+
"Nemoj prikazivati razgovor u oblačićima"
"Chatajte koristeći oblačiće"
"Novi razgovori se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da otvorite oblačić. Prevucite da ga premjestite."
@@ -127,6 +129,5 @@
"Otvaranje menija"
"Maksimiziraj ekran"
"Snimi ekran"
-
-
+ "Nije moguće promijeniti veličinu aplikacije"
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 5dd65ddccbcf18a13b09d1ce370a6e97809e9d63..fa4b627805c0c7236bb7b34ef16689009ab2fc3a 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -73,6 +73,8 @@
"replega %1$s"
"Configuració de l\'aplicació %1$s"
"Ignora la bombolla"
+
+
"No mostris la conversa com a bombolla"
"Xateja amb bombolles"
"Les converses noves es mostren com a icones flotants o bombolles. Toca per obrir una bombolla. Arrossega-la per moure-la."
@@ -127,6 +129,5 @@
"Obre el menú"
"Maximitza la pantalla"
"Ajusta la pantalla"
-
-
+ "No es pot canviar la mida d\'aquesta aplicació"
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index b75dbfcaad5b95c3206b6f780064977df62c9ba1..3956fcacbb5026b98483e2e2a35e2494193e838c 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -73,6 +73,8 @@
"sbalit %1$s"
"Nastavení %1$s"
"Zavřít bublinu"
+
+
"Nezobrazovat konverzaci v bublinách"
"Chatujte pomocí bublin"
"Nové konverzace se zobrazují jako plovoucí ikony, neboli bubliny. Klepnutím bublinu otevřete. Přetažením ji posunete."
@@ -127,6 +129,5 @@
"Otevřít nabídku"
"Maximalizovat obrazovku"
"Rozpůlit obrazovku"
-
-
+ "Velikost aplikace nelze změnit"
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 40ad85920dbdf9e627d2aab43f650c1cf108f3e3..afe4a1ab877c60c73fab6918375b5944667f9d54 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -73,6 +73,8 @@
"skjul %1$s"
"Indstillinger for %1$s"
"Afvis boble"
+
+
"Vis ikke samtaler i bobler"
"Chat ved hjælp af bobler"
"Nye samtaler vises som svævende ikoner eller bobler. Tryk for at åbne boblen. Træk for at flytte den."
@@ -127,6 +129,5 @@
"Åbn menu"
"Maksimér skærm"
"Tilpas skærm"
-
-
+ "Størrelsen på denne app kan ikke justeres"
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 9389f3e76a1d921e70e382c31296b894a4b30643..1e503393eb8d65634e65fa9ec1a7578ae60758cf 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -73,6 +73,8 @@
"%1$s minimieren"
"Einstellungen für %1$s"
"Bubble schließen"
+
+
"Unterhaltung nicht als Bubble anzeigen"
"Bubbles zum Chatten verwenden"
"Neue Unterhaltungen erscheinen als unverankerte Symbole, „Bubbles“ genannt. Wenn du eine Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."
@@ -127,6 +129,5 @@
"Menü öffnen"
"Bildschirm maximieren"
"Bildschirm teilen"
-
-
+ "Die Größe dieser App kann nicht geändert werden"
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index a9a14bf044fb47bf76fe025dad313224df8b3f70..5c3c6dedd6327e16048804a53088d1af1d960597 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -73,6 +73,8 @@
"σύμπτυξη %1$s"
"Ρυθμίσεις %1$s"
"Παράβλ. για συννεφ."
+
+
"Να μην γίνει προβολή της συζήτησης σε συννεφάκια."
"Συζητήστε χρησιμοποιώντας συννεφάκια."
"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή συννεφάκια. Πατήστε για να ανοίξετε το συννεφάκι. Σύρετε για να το μετακινήσετε."
@@ -127,6 +129,5 @@
"Άνοιγμα μενού"
"Μεγιστοποίηση οθόνης"
"Προβολή στο μισό της οθόνης"
-
-
+ "Δεν είναι δυνατή η αλλαγή μεγέθους αυτής της εφαρμογής"
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 78a2c9e661c8197663401fd82b70bab802e4af09..51c69e559f1635c90c9244f5edcc0dc64a5cc89e 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -73,6 +73,8 @@
"collapse %1$s"
"%1$s settings"
"Dismiss bubble"
+
+
"Don’t bubble conversation"
"Chat using bubbles"
"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."
@@ -127,6 +129,5 @@
"Open menu"
"Maximise screen"
"Snap screen"
-
-
+ "This app can\'t be resized"
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index d11f521a187eac044ad0115ad33c4954192a344e..f5b0a27f9808dc960fa4b975b5db73f942b00918 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -73,6 +73,7 @@
"collapse %1$s"
"%1$s settings"
"Dismiss bubble"
+ "Move to fullscreen"
"Don’t bubble conversation"
"Chat using bubbles"
"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 78a2c9e661c8197663401fd82b70bab802e4af09..51c69e559f1635c90c9244f5edcc0dc64a5cc89e 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -73,6 +73,8 @@
"collapse %1$s"
"%1$s settings"
"Dismiss bubble"
+
+
"Don’t bubble conversation"
"Chat using bubbles"
"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."
@@ -127,6 +129,5 @@
"Open menu"
"Maximise screen"
"Snap screen"
-
-
+ "This app can\'t be resized"
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 78a2c9e661c8197663401fd82b70bab802e4af09..51c69e559f1635c90c9244f5edcc0dc64a5cc89e 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -73,6 +73,8 @@
"collapse %1$s"
"%1$s settings"
"Dismiss bubble"
+
+
"Don’t bubble conversation"
"Chat using bubbles"
"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."
@@ -127,6 +129,5 @@
"Open menu"
"Maximise screen"
"Snap screen"
-
-
+ "This app can\'t be resized"
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 8002468a3659b102a02e5891db3d52de97352872..6292be50591001fb245e92fce87a85dae92ddd01 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -73,6 +73,7 @@
"collapse %1$s"
"%1$s settings"
"Dismiss bubble"
+ "Move to fullscreen"
"Don’t bubble conversation"
"Chat using bubbles"
"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 8dadc70b8c9dc8a7e3a2e07470223810dd637097..8644780dba03834233963e7a90c15787e6646190 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -73,6 +73,8 @@
"contraer %1$s"
"Configuración de %1$s"
"Descartar burbuja"
+
+
"No mostrar la conversación en burbuja"
"Chat con burbujas"
"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."
@@ -127,6 +129,5 @@
"Abrir el menú"
"Maximizar pantalla"
"Ajustar pantalla"
-
-
+ "No se puede cambiar el tamaño de esta app"
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 33eba9300542d335f577d58e200cb3fadc34e952..9718bf19fcc3ce0107f82a38694546b0d6e7ac30 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -73,6 +73,8 @@
"contraer %1$s"
"Ajustes de %1$s"
"Cerrar burbuja"
+
+
"No mostrar conversación en burbuja"
"Chatea con burbujas"
"Las conversaciones nuevas aparecen como iconos flotantes llamados \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."
@@ -127,6 +129,5 @@
"Abrir menú"
"Maximizar pantalla"
"Ajustar pantalla"
-
-
+ "No se puede cambiar el tamaño de esta aplicación"
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index e76f6e882afc9fb69299c6ce99ee1a27c52932a5..d36a8d14e76bc3853a499f900a359bc7c996628e 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -73,6 +73,8 @@
"ahenda %1$s"
"Rakenduse %1$s seaded"
"Sule mull"
+
+
"Ära kuva vestlust mullina"
"Vestelge mullide abil"
"Uued vestlused kuvatakse hõljuvate ikoonidena ehk mullidena. Puudutage mulli avamiseks. Lohistage mulli, et seda liigutada."
@@ -127,6 +129,5 @@
"Ava menüü"
"Kuva täisekraanil"
"Kuva poolel ekraanil"
-
-
+ "Selle rakenduse aknasuurust ei saa muuta"
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 961c784f58a8273ca041bc33f8f6a123feddd431..2ee086e47429bc43e0a7e66166f589c88a2e6b72 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -73,6 +73,8 @@
"tolestu %1$s"
"%1$s aplikazioaren ezarpenak"
"Baztertu burbuila"
+
+
"Ez erakutsi elkarrizketak burbuila gisa"
"Txateatu burbuilen bidez"
"Elkarrizketa berriak ikono gainerakor edo burbuila gisa agertzen dira. Sakatu burbuila irekitzeko. Arrasta ezazu mugitzeko."
@@ -80,7 +82,7 @@
"Aplikazioaren burbuilak desaktibatzeko, sakatu Kudeatu"
"Ados"
"Ez dago azkenaldiko burbuilarik"
- "Azken burbuilak eta baztertutakoak agertuko dira hemen"
+ "Azkenaldiko burbuilak eta baztertutakoak agertuko dira hemen"
"Txateatu burbuilak erabilita"
"Elkarrizketa berriak ikono gisa agertzen dira pantailaren beheko izkinan. Zabaltzeko, saka itzazu. Baztertzeko, aldiz, arrasta itzazu."
"Kontrolatu burbuilak edonoiz"
@@ -127,6 +129,5 @@
"Ireki menua"
"Handitu pantaila"
"Zatitu pantaila"
-
-
+ "Ezin zaio aldatu tamaina aplikazio honi"
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index c004ea8f219779c5a3a35617ee0f18a55b2ed387..f4cdd5f6672d3c164744ed87d347cea12d309d9e 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -73,6 +73,8 @@
"جمع کردن %1$s"
"تنظیمات %1$s"
"رد کردن حبابک"
+
+
"مکالمه در حباب نشان داده نشود"
"گپ بااستفاده از حبابکها"
"مکالمههای جدید بهصورت نمادهای شناور یا حبابکها نشان داده میشوند. برای باز کردن حبابکها تکضرب بزنید. برای جابهجایی، آن را بکشید."
@@ -127,6 +129,5 @@
"باز کردن منو"
"بزرگ کردن صفحه"
"بزرگ کردن صفحه"
-
-
+ "اندازه این برنامه را نمیتوان تغییر داد"
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 59cd6e0c1e2ab1720d66134f2fe55b76d780ec62..6be2ee2f00b0530859e77a306cedb7d9c9e3c777 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -73,6 +73,8 @@
"tiivistä %1$s"
"%1$s: asetukset"
"Ohita kupla"
+
+
"Älä näytä kuplia keskusteluista"
"Chattaile kuplien avulla"
"Uudet keskustelut näkyvät kelluvina kuvakkeina tai kuplina. Avaa kupla napauttamalla. Siirrä sitä vetämällä."
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 288dbb287220c875a386e4687b133e96172d6d78..54700990bf6c6f2f9ebcc58026311879dbe2658f 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -73,6 +73,8 @@
"réduire %1$s"
"Paramètres %1$s"
"Ignorer la bulle"
+
+
"Ne pas afficher les conversations dans des bulles"
"Clavarder en utilisant des bulles"
"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes (de bulles). Touchez une bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."
@@ -127,6 +129,5 @@
"Ouvrir le menu"
"Agrandir l\'écran"
"Aligner l\'écran"
-
-
+ "Impossible de redimensionner cette appli"
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 7a7e592626700e9c5f12fcc42ef0067e7d59bee9..63b5994cd70700864b0a2e0c4af4ffc69163636d 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -73,6 +73,8 @@
"Réduire %1$s"
"Paramètres %1$s"
"Fermer la bulle"
+
+
"Ne pas afficher la conversation dans une bulle"
"Chatter en utilisant des bulles"
"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou de bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."
@@ -127,6 +129,5 @@
"Ouvrir le menu"
"Mettre en plein écran"
"Fractionner l\'écran"
-
-
+ "Impossible de redimensionner cette appli"
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 8f3b132defb73aa7f816056e437ffdb3e50fd541..36ad5210833715b5d4af74832946bd3ff364c28e 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -73,6 +73,8 @@
"contraer %1$s"
"Configuración de %1$s"
"Ignorar burbulla"
+
+
"Non mostrar a conversa como burbulla"
"Chatear usando burbullas"
"As conversas novas aparecen como iconas flotantes ou burbullas. Toca para abrir a burbulla e arrastra para movela."
@@ -127,6 +129,5 @@
"Abrir menú"
"Maximizar pantalla"
"Encaixar pantalla"
-
-
+ "Non se pode cambiar o tamaño desta aplicación"
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 78eebd6ed58df023a89197f73ae53535ef9dfac3..868ef5b077aefe976da305cf0caa58c2347cc83d 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -73,6 +73,8 @@
"%1$s નાનું કરો"
"%1$s સેટિંગ"
"બબલને છોડી દો"
+
+
"વાતચીતને બબલ કરશો નહીં"
"બબલનો ઉપયોગ કરીને ચૅટ કરો"
"નવી વાતચીત ફ્લોટિંગ આઇકન અથવા બબલ જેવી દેખાશે. બબલને ખોલવા માટે ટૅપ કરો. તેને ખસેડવા માટે ખેંચો."
@@ -127,6 +129,5 @@
"મેનૂ ખોલો"
"સ્ક્રીન કરો મોટી કરો"
"સ્ક્રીન સ્નૅપ કરો"
-
-
+ "આ ઍપના કદમાં વધઘટ કરી શકાતો નથી"
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index f889f2091255a4884cf7fb90719b39b4f034260d..31c7307688af1a9ecc4ec9a47d6ca75ffb4cb018 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -73,6 +73,8 @@
"%1$s को छोटा करें"
"%1$s की सेटिंग"
"बबल खारिज करें"
+
+
"बातचीत को बबल न करें"
"बबल्स का इस्तेमाल करके चैट करें"
"नई बातचीत फ़्लोटिंग आइकॉन या बबल्स की तरह दिखेंगी. बबल को खोलने के लिए टैप करें. इसे एक जगह से दूसरी जगह ले जाने के लिए खींचें और छोड़ें."
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index dca2431c7f74572c85bcb864b006cc55fdbd68f4..d99a65d891074192dddabad7bb61f22404847975 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -73,6 +73,8 @@
"sažmite oblačić %1$s"
"Postavke za %1$s"
"Odbaci oblačić"
+
+
"Zaustavi razgovor u oblačićima"
"Oblačići u chatu"
"Novi razgovori pojavljuju se kao pomične ikone ili oblačići. Dodirnite za otvaranje oblačića. Povucite da biste ga premjestili."
@@ -127,6 +129,5 @@
"Otvaranje izbornika"
"Maksimalno povećaj zaslon"
"Izradi snimku zaslona"
-
-
+ "Nije moguće promijeniti veličinu aplikacije"
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index a0e9ec5ffc59ae39d3bc85b69842ed6dcd82c606..bed760eb5ed7849e5fdf8804cb657bb06a8ce32f 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -73,6 +73,8 @@
"%1$s összecsukása"
"%1$s beállításai"
"Buborék elvetése"
+
+
"Ne jelenjen meg a beszélgetés buborékban"
"Buborékokat használó csevegés"
"Az új beszélgetések lebegő ikonként, vagyis buborékként jelennek meg. A buborék megnyitásához koppintson rá. Áthelyezéshez húzza a kívánt helyre."
@@ -127,6 +129,5 @@
"Menü megnyitása"
"Képernyő méretének maximalizálása"
"Igazodás a képernyő adott részéhez"
-
-
+ "Ezt az alkalmazást nem lehet átméretezni"
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 2208a5ce610a9f0c5d1a39358bdd0c54ff0ff02a..fcb72543228662ab9f11ceccc3cfc8faa8cab6b2 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -73,6 +73,8 @@
"%1$s. ծալել"
"%1$s – կարգավորումներ"
"Փակել ամպիկը"
+
+
"Զրույցը չցուցադրել ամպիկի տեսքով"
"Զրույցի ամպիկներ"
"Նոր զրույցները կհայտնվեն լողացող պատկերակների կամ ամպիկների տեսքով։ Հպեք՝ ամպիկը բացելու համար։ Քաշեք՝ այն տեղափոխելու համար։"
@@ -127,6 +129,5 @@
"Բացել ընտրացանկը"
"Ծավալել էկրանը"
"Ծալել էկրանը"
-
-
+ "Այս հավելվածի չափը հնարավոր չէ փոխել"
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 11de8176a585b96c3703da2a874296c89c938a6f..85a9bbf253a516d35e9760238265c9ed71508dc6 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -73,6 +73,8 @@
"ciutkan %1$s"
"Setelan %1$s"
"Tutup balon"
+
+
"Jangan gunakan percakapan balon"
"Chat dalam tampilan balon"
"Percakapan baru muncul sebagai ikon mengambang, atau balon. Ketuk untuk membuka balon. Tarik untuk memindahkannya."
@@ -127,6 +129,5 @@
"Buka Menu"
"Perbesar Layar"
"Gabungkan Layar"
-
-
+ "Ukuran aplikasi ini tidak dapat diubah"
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index ad679a8dd48be07d2faf9aca0f586b077efbc566..8041162f7b5c09c29008a97782ede98fa0576f8e 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -73,6 +73,8 @@
"minnka %1$s"
"Stillingar %1$s"
"Loka blöðru"
+
+
"Ekki setja samtal í blöðru"
"Spjalla með blöðrum"
"Ný samtöl birtast sem fljótandi tákn eða blöðrur. Ýttu til að opna blöðru. Dragðu hana til að færa."
@@ -127,6 +129,5 @@
"Opna valmynd"
"Stækka skjá"
"Smelluskjár"
-
-
+ "Ekki er hægt að breyta stærð þessa forrits"
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 4ae4b36d34d5d3f1b614c5dea6e01690180fe591..3ba6873617f63543db505275ff0369b859da2a7f 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -73,6 +73,8 @@
"comprimi %1$s"
"Impostazioni %1$s"
"Ignora bolla"
+
+
"Non mettere la conversazione nella bolla"
"Chatta utilizzando le bolle"
"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index cccb71fd3f4961d433f48f6d82cf09bd9fa704ac..e1854fa163037574302412001dea661f379f4fcc 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -73,6 +73,8 @@
"כיווץ של %1$s"
"הגדרות %1$s"
"סגירת בועה"
+
+
"אין להציג בועות לשיחה"
"לדבר בבועות"
"שיחות חדשות מופיעות כסמלים צפים, או בועות. יש להקיש כדי לפתוח בועה. יש לגרור כדי להזיז אותה."
@@ -127,6 +129,5 @@
"פתיחת התפריט"
"הגדלת המסך"
"כיווץ המסך"
-
-
+ "לא ניתן לשנות את גודל החלון של האפליקציה הזו"
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 0cb921ccf810ccab93050f0083a4d681c9b1fe5f..1f1ddc71efa8ccf255a2a62cc3f172d8c66cac73 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -73,6 +73,8 @@
"%1$sを閉じます"
"%1$s の設定"
"バブルを閉じる"
+
+
"会話をバブルで表示しない"
"チャットでバブルを使う"
"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 16e99ba9d46b5dbf4144536869c716f6414175c7..e201a2023d33773d99a87711c35ea8aa00939bce 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -73,6 +73,8 @@
"%1$s-ის ჩაკეცვა"
"%1$s-ის პარამეტრები"
"ბუშტის დახურვა"
+
+
"აიკრძალოს საუბრის ბუშტები"
"ჩეთი ბუშტების გამოყენებით"
"ახალი საუბრები გამოჩნდება როგორც მოტივტივე ხატულები ან ბუშტები. შეეხეთ ბუშტის გასახსნელად. გადაიტანეთ ჩავლებით."
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index a46cc810c3e795345ce83a12d9fd2c067131ae32..1c335dd4714d9040ab62b488e58dcdf6c3a0c6d2 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -73,6 +73,8 @@
"%1$s: жию"
"%1$s параметрлері"
"Қалқымалы хабарды жабу"
+
+
"Әңгіменің қалқыма хабары көрсетілмесін"
"Қалқыма хабарлар арқылы сөйлесу"
"Жаңа әңгімелер қалқыма белгішелер немесе хабарлар түрінде көрсетіледі. Қалқыма хабарды ашу үшін түртіңіз. Жылжыту үшін сүйреңіз."
@@ -127,6 +129,5 @@
"Мәзірді ашу"
"Экранды ұлғайту"
"Экранды бөлу"
-
-
+ "Бұл қолданбаның өлшемі өзгертілмейді."
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 1187567d6a26859babb43df493d287575c087a6d..d0cceee8aba5cdda24c0980c9059f689a4af0913 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -73,6 +73,8 @@
"បង្រួម %1$s"
"ការកំណត់ %1$s"
"ច្រានចោលពពុះ"
+
+
"កុំបង្ហាញការសន្ទនាជាពពុះ"
"ជជែកដោយប្រើពពុះ"
"ការសន្ទនាថ្មីៗបង្ហាញជាពពុះ ឬរូបអណ្ដែត។ ចុច ដើម្បីបើកពពុះ។ អូស ដើម្បីផ្លាស់ទីពពុះនេះ។"
@@ -127,6 +129,5 @@
"បើកម៉ឺនុយ"
"ពង្រីកអេក្រង់"
"ថតអេក្រង់"
-
-
+ "មិនអាចប្ដូរទំហំកម្មវិធីនេះបានទេ"
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index c1ae69acf9441575f945c7e6dcaefc7c653fc503..63b5c68a96fca7f841ce57be59ab7cdf2aea43f0 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -38,16 +38,16 @@
"ಸೆಕೆಂಡರಿ ಡಿಸ್ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."
"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"
"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"
- "ಎಡ ಪೂರ್ಣ ಪರದೆ"
+ "ಎಡ ಫುಲ್ ಸ್ಕ್ರೀನ್"
"70% ಎಡಕ್ಕೆ"
"50% ಎಡಕ್ಕೆ"
"30% ಎಡಕ್ಕೆ"
- "ಬಲ ಪೂರ್ಣ ಪರದೆ"
- "ಮೇಲಿನ ಪೂರ್ಣ ಪರದೆ"
+ "ಬಲ ಫುಲ್ ಸ್ಕ್ರೀನ್"
+ "ಮೇಲಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"
"70% ಮೇಲಕ್ಕೆ"
"50% ಮೇಲಕ್ಕೆ"
"30% ಮೇಲಕ್ಕೆ"
- "ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆ"
+ "ಕೆಳಗಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"
"ಎಡಕ್ಕೆ ವಿಭಜಿಸಿ"
"ಬಲಕ್ಕೆ ವಿಭಜಿಸಿ"
"ಮೇಲಕ್ಕೆ ವಿಭಜಿಸಿ"
@@ -73,6 +73,8 @@
"%1$s ಅನ್ನು ಕುಗ್ಗಿಸಿ"
"%1$s ಸೆಟ್ಟಿಂಗ್ಗಳು"
"ಬಬಲ್ ವಜಾಗೊಳಿಸಿ"
+
+
"ಸಂಭಾಷಣೆಯನ್ನು ಬಬಲ್ ಮಾಡಬೇಡಿ"
"ಬಬಲ್ಸ್ ಬಳಸಿ ಚಾಟ್ ಮಾಡಿ"
"ಹೊಸ ಸಂಭಾಷಣೆಗಳು ತೇಲುವ ಐಕಾನ್ಗಳು ಅಥವಾ ಬಬಲ್ಸ್ ಆಗಿ ಗೋಚರಿಸುತ್ತವೆ. ಬಬಲ್ ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಅದನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಲು ಎಳೆಯಿರಿ."
@@ -127,6 +129,5 @@
"ಮೆನು ತೆರೆಯಿರಿ"
"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"
"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"
-
-
+ "ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
index 3dfe573a6506d048a707ff62c27879b42f37c775..efb79306cb3bc69e78c24571f88146926bf102fe 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -20,7 +20,7 @@
"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ"
"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"
"ಮುಚ್ಚಿರಿ"
- "ಪೂರ್ಣ ಪರದೆ"
+ "ಫುಲ್ ಸ್ಕ್ರೀನ್"
"ಸರಿಸಿ"
"ವಿಸ್ತೃತಗೊಳಿಸಿ"
"ಕುಗ್ಗಿಸಿ"
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 5ff159d5ece6b226132357a90ec6e6c03b8e2ce1..b5efd10ad19f0f176ae9b8d9fb13a621e5c04ca7 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -73,6 +73,8 @@
"%1$s 접기"
"%1$s 설정"
"대화창 닫기"
+
+
"대화를 대화창으로 표시하지 않기"
"대화창으로 채팅하기"
"새로운 대화가 플로팅 아이콘인 대화창으로 표시됩니다. 대화창을 열려면 탭하세요. 드래그하여 이동할 수 있습니다."
@@ -127,6 +129,5 @@
"메뉴 열기"
"화면 최대화"
"화면 분할"
-
-
+ "이 앱은 크기를 조절할 수 없습니다."
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 958a2399eba5c899b669985ebbbc250160af0d80..e001efe9296a3d32e2b12b6005fb93bcbc4f5a62 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -73,6 +73,8 @@
"%1$s жыйыштыруу"
"%1$s параметрлери"
"Калкып чыкма билдирмени жабуу"
+
+
"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"
"Калкып чыкма билдирмелер аркылуу маектешүү"
"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн тийип коюңуз. Жылдыруу үчүн сүйрөңүз."
@@ -127,6 +129,5 @@
"Менюну ачуу"
"Экранды чоңойтуу"
"Экранды сүрөткө тартып алуу"
-
-
+ "Бул колдонмонун өлчөмүн өзгөртүүгө болбойт"
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index ed6b378c574a9b1fb574001d074ebe5e761b8859..029c95b18ce993bc8192c0a5e058f85283b12e58 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -73,6 +73,8 @@
"ຫຍໍ້ %1$s ລົງ"
"ການຕັ້ງຄ່າ %1$s"
"ປິດຟອງໄວ້"
+
+
"ຢ່າໃຊ້ຟອງໃນການສົນທະນາ"
"ສົນທະນາໂດຍໃຊ້ຟອງ"
"ການສົນທະນາໃໝ່ຈະປາກົດເປັນໄອຄອນ ຫຼື ຟອງແບບລອຍ. ແຕະເພື່ອເປີດຟອງ. ລາກເພື່ອຍ້າຍມັນ."
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 86fc0d83ea284cdc65dbb180626f12884cfb39b7..791ddcd48933b7c797e5245abbddf5b659997e4c 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -73,6 +73,8 @@
"sutraukti „%1$s“"
"„%1$s“ nustatymai"
"Atsisakyti burbulo"
+
+
"Nerodyti pokalbio burbule"
"Pokalbis naudojant burbulus"
"Nauji pokalbiai rodomi kaip slankiosios piktogramos arba burbulai. Palieskite, kad atidarytumėte burbulą. Vilkite, kad perkeltumėte."
@@ -127,6 +129,5 @@
"Atidaryti meniu"
"Išskleisti ekraną"
"Sutraukti ekraną"
-
-
+ "Negalima keisti šios programos dydžio"
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index a16cc527ec31d9f46afd1e181812e9d4d82ae9b3..8a86687ebe8eeb04ef6db70ba7f9b05c2d5f65f6 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -73,6 +73,8 @@
"Sakļaut “%1$s”"
"Lietotnes %1$s iestatījumi"
"Nerādīt burbuli"
+
+
"Nerādīt sarunu burbuļos"
"Tērzēšana, izmantojot burbuļus"
"Jaunas sarunas tiek rādītas kā peldošas ikonas vai burbuļi. Pieskarieties, lai atvērtu burbuli. Velciet, lai to pārvietotu."
@@ -127,6 +129,5 @@
"Atvērt izvēlni"
"Maksimizēt ekrānu"
"Fiksēt ekrānu"
-
-
+ "Šīs lietotnes loga lielumu nevar mainīt."
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 2878c45237a9c627a5c7e267d19f9f13a82e6da0..3a6b2f0a1e4fc0704b26d27c7a0eb90acbb20a7c 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -73,6 +73,8 @@
"собери %1$s"
"Поставки за %1$s"
"Отфрли балонче"
+
+
"Не прикажувај го разговорот во балончиња"
"Разговор во балончиња"
"Новите разговори ќе се појавуваат како лебдечки икони или балончиња. Допрете за отворање на балончето. Повлечете за да го преместите."
@@ -127,6 +129,5 @@
"Отвори го менито"
"Максимизирај го екранот"
"Подели го екранот на половина"
-
-
+ "Не може да се промени големината на апликацијава"
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 6e7ea08b76109483b5f23f779e8d647fd1cdd838..26e4a4661d89ea81007f9e4ff13e9302be6cd056 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -73,6 +73,8 @@
"%1$s ചുരുക്കുക"
"%1$s ക്രമീകരണം"
"ബബിൾ ഡിസ്മിസ് ചെയ്യൂ"
+
+
"സംഭാഷണം ബബിൾ ചെയ്യരുത്"
"ബബിളുകൾ ഉപയോഗിച്ച് ചാറ്റ് ചെയ്യുക"
"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ഇത് നീക്കാൻ വലിച്ചിടുക."
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index e64edb1d7ffe86e1781c25e495d8ebba608024af..505a4ad66c962911c4309a9180056310eda334cb 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -73,6 +73,8 @@
"%1$s-г хураах"
"%1$s-н тохиргоо"
"Бөмбөлгийг хаах"
+
+
"Харилцан яриаг бүү бөмбөлөг болго"
"Бөмбөлөг ашиглан чатлаарай"
"Шинэ харилцан яриа нь хөвөгч дүрс тэмдэг эсвэл бөмбөлөг хэлбэрээр харагддаг. Бөмбөлгийг нээхийн тулд товшино уу. Түүнийг зөөхийн тулд чирнэ үү."
@@ -127,6 +129,5 @@
"Цэс нээх"
"Дэлгэцийг томруулах"
"Дэлгэцийг таллах"
-
-
+ "Энэ аппын хэмжээг өөрчлөх боломжгүй"
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 14a6b0e71d37ef21bd9bd7e663ec490ad21de1d2..cc35f11fa3087b59945ea99b0e732abaf5963c4e 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -73,6 +73,8 @@
"%1$s कोलॅप्स करा"
"%1$s सेटिंग्ज"
"बबल डिसमिस करा"
+
+
"संभाषणाला बबल करू नका"
"बबल वापरून चॅट करा"
"नवीन संभाषणे फ्लोटिंग आयकन किंवा बबल म्हणून दिसतात. बबल उघडण्यासाठी टॅप करा. हे हलवण्यासाठी ड्रॅग करा."
@@ -127,6 +129,5 @@
"मेनू उघडा"
"स्क्रीन मोठी करा"
"स्क्रीन स्नॅप करा"
-
-
+ "या अॅपचा आकार बदलला जाऊ शकत नाही"
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 7e80c846f962be5b1f0c61eb006708b19c51dcb6..61d66144cb0c8192b715f9c710f3e0c79c1faef2 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -73,6 +73,8 @@
"kuncupkan %1$s"
"Tetapan %1$s"
"Ketepikan gelembung"
+
+
"Jangan jadikan perbualan dalam bentuk gelembung"
"Bersembang menggunakan gelembung"
"Perbualan baharu muncul sebagai ikon terapung atau gelembung. Ketik untuk membuka gelembung. Seret untuk mengalihkan gelembung tersebut."
@@ -127,6 +129,5 @@
"Buka Menu"
"Maksimumkan Skrin"
"Tangkap Skrin"
-
-
+ "Apl ini tidak boleh diubah saiz"
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 32f323497d9edf25559f008feaa4e9be89a45696..7841e07aa9496c4e1c04ff1aeba1a51d3ab93e42 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -73,6 +73,8 @@
"%1$s ကို ချုံ့ရန်"
"%1$s ဆက်တင်များ"
"ပူဖောင်းကွက် ပယ်ရန်"
+
+
"စကားဝိုင်းကို ပူဖောင်းကွက် မပြုလုပ်ပါနှင့်"
"ပူဖောင်းကွက် သုံး၍ ချတ်လုပ်ခြင်း"
"စကားဝိုင်းအသစ်များကို မျောနေသည့် သင်္ကေတများ သို့မဟုတ် ပူဖောင်းကွက်များအဖြစ် မြင်ရပါမည်။ ပူဖောင်းကွက်ကိုဖွင့်ရန် တို့ပါ။ ရွှေ့ရန် ၎င်းကို ဖိဆွဲပါ။"
@@ -127,6 +129,5 @@
"မီနူး ဖွင့်ရန်"
"စခရင်ကို ချဲ့မည်"
"စခရင်ကို ချုံ့မည်"
-
-
+ "ဤအက်ပ်ကို အရွယ်ပြင်၍ မရပါ"
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index f965a50dbd88bd071a937c2c4b74987b0f766d18..ea2ae1ba1db4f28f487907d2572af9165d0d85ac 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -73,6 +73,8 @@
"skjul %1$s"
"%1$s-innstillinger"
"Lukk boblen"
+
+
"Ikke vis samtaler i bobler"
"Chat med bobler"
"Nye samtaler vises som flytende ikoner eller bobler. Trykk for å åpne en boble. Dra for å flytte den."
@@ -127,6 +129,5 @@
"Åpne menyen"
"Maksimer skjermen"
"Fest skjermen"
-
-
+ "Du kan ikke endre størrelse på denne appen"
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 113085e18f2fede2d53a340421b674cfc29aa8b2..a3bd5eec6d5ee5dc5ba0ed3ab52eea65dc13ef09 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -73,6 +73,8 @@
"%1$s कोल्याप्स गर्नुहोस्"
"%1$s का सेटिङहरू"
"बबल खारेज गर्नुहोस्"
+
+
"वार्तालाप बबलको रूपमा नदेखाउनुहोस्"
"बबलहरू प्रयोग गरी कुराकानी गर्नुहोस्"
"नयाँ वार्तालापहरू तैरने आइकन वा बबलका रूपमा देखिन्छन्। बबल खोल्न ट्याप गर्नुहोस्। बबल सार्न सो बबललाई ड्र्याग गर्नुहोस्।"
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 32c87d5a3a5eb12851e6d6c8a6e538e90d892655..ad2c60fe4517516ffdbd0320b4b687e1326cf928 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -73,6 +73,8 @@
"%1$s samenvouwen"
"Instellingen voor %1$s"
"Bubbel sluiten"
+
+
"Gesprekken niet in bubbels tonen"
"Chatten met bubbels"
"Nieuwe gesprekken worden als zwevende iconen of bubbels getoond. Tik om een bubbel te openen. Sleep om een bubbel te verplaatsen."
@@ -127,6 +129,5 @@
"Menu openen"
"Scherm maximaliseren"
"Scherm halveren"
-
-
+ "Het formaat van deze app kan niet worden aangepast"
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 9151dea3ce4ffd4fe8ef94bae6c2b34406c86a2a..76f2715b54c9268165e55d92df57b4715681c8fa 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -66,13 +66,15 @@
"ତଳ ବାମକୁ ନିଅନ୍ତୁ"
"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"
"ମେନୁକୁ ବିସ୍ତାର କରନ୍ତୁ"
- "ମେନୁକୁ ସଂକୁଚିତ କରନ୍ତୁ"
+ "ମେନୁକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"
"ବାମକୁ ମୁଭ କରନ୍ତୁ"
"ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"
"%1$s ବିସ୍ତାର କରନ୍ତୁ"
"%1$s ସଙ୍କୁଚିତ କରନ୍ତୁ"
"%1$s ସେଟିଂସ୍"
"ବବଲ୍ ଖାରଜ କରନ୍ତୁ"
+
+
"ବାର୍ତ୍ତାଳାପକୁ ବବଲ୍ କରନ୍ତୁ ନାହିଁ"
"ବବଲଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଚାଟ୍ କରନ୍ତୁ"
"ନୂଆ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଫ୍ଲୋଟିଂ ଆଇକନ୍ କିମ୍ବା ବବଲ୍ ଭାବେ ଦେଖାଯିବ। ବବଲ୍ ଖୋଲିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଏହାକୁ ମୁଭ୍ କରିବାକୁ ଟାଣନ୍ତୁ।"
@@ -127,6 +129,5 @@
"ମେନୁ ଖୋଲନ୍ତୁ"
"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"
"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"
-
-
+ "ଏହି ଆପକୁ ରିସାଇଜ କରାଯାଇପାରିବ ନାହିଁ"
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index e099cc9858739ae7b2b3ed9c62c1a7d2dc633b36..cd7fd4716c6551f099b638fd0ef381b407cf665c 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -73,6 +73,8 @@
"%1$s ਨੂੰ ਸਮੇਟੋ"
"%1$s ਸੈਟਿੰਗਾਂ"
"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕਰੋ"
+
+
"ਗੱਲਬਾਤ \'ਤੇ ਬਬਲ ਨਾ ਲਾਓ"
"ਬਬਲ ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"
"ਨਵੀਆਂ ਗੱਲਾਂਬਾਤਾਂ ਫਲੋਟਿੰਗ ਪ੍ਰਤੀਕਾਂ ਜਾਂ ਬਬਲ ਦੇ ਰੂਪ ਵਿੱਚ ਦਿਸਦੀਆਂ ਹਨ। ਬਬਲ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਇਸਨੂੰ ਲਿਜਾਣ ਲਈ ਘਸੀਟੋ।"
@@ -127,6 +129,5 @@
"ਮੀਨੂ ਖੋਲ੍ਹੋ"
"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"
"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"
-
-
+ "ਇਸ ਐਪ ਦਾ ਆਕਾਰ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index f954c9d6d2907fb26b1614a911c9011fed00bcd6..d33b6f15ba37fe764e5a28c5a70f78dd293bf874 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -73,6 +73,8 @@
"zwiń dymek %1$s"
"%1$s – ustawienia"
"Zamknij dymek"
+
+
"Nie wyświetlaj rozmowy jako dymka"
"Czatuj, korzystając z dymków"
"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."
@@ -127,6 +129,5 @@
"Otwórz menu"
"Maksymalizuj ekran"
"Przyciągnij ekran"
-
-
+ "Nie można zmienić rozmiaru tej aplikacji"
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 1e980159dcd293683568d28ca98f814a88682ec8..c7a00ff3257eb9633c78c17e051de46554cc513d 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -73,6 +73,8 @@
"fechar %1$s"
"Configurações de %1$s"
"Dispensar balão"
+
+
"Não criar balões de conversa"
"Converse usando balões"
"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."
@@ -127,6 +129,5 @@
"Abrir o menu"
"Ampliar tela"
"Ajustar tela"
-
-
+ "Não é possível redimensionar o app"
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index f433413c9a9b5dfa7c33c29d3533bead7dca1800..d14144705c563b2649db3de29245ab6801e39d05 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -73,6 +73,8 @@
"reduzir %1$s"
"Definições de %1$s"
"Ignorar balão"
+
+
"Não apresentar a conversa em balões"
"Converse no chat através de balões"
"As novas conversas aparecem como ícones flutuantes ou balões. Toque para abrir o balão. Arraste para o mover."
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 1e980159dcd293683568d28ca98f814a88682ec8..c7a00ff3257eb9633c78c17e051de46554cc513d 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -73,6 +73,8 @@
"fechar %1$s"
"Configurações de %1$s"
"Dispensar balão"
+
+
"Não criar balões de conversa"
"Converse usando balões"
"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."
@@ -127,6 +129,5 @@
"Abrir o menu"
"Ampliar tela"
"Ajustar tela"
-
-
+ "Não é possível redimensionar o app"
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 0b96492f6c5faaf06cde7d3b27cf8cbb03de78b0..9bc76602e1d33c3b9d198a21d854874d2d2223a5 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -73,6 +73,8 @@
"restrânge %1$s"
"Setări %1$s"
"Închide balonul"
+
+
"Nu afișa conversația în balon"
"Chat cu baloane"
"Conversațiile noi apar ca pictograme flotante sau baloane. Atinge pentru a deschide balonul. Trage pentru a-l muta."
@@ -127,6 +129,5 @@
"Deschide meniul"
"Maximizează fereastra"
"Micșorează fereastra și fixeaz-o"
-
-
+ "Aplicația nu poate fi redimensionată"
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index a1b39e430595237425b416a35f1d758a68603df6..044e3b02d4395c95e859cbb18ca392b8e6066522 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -73,6 +73,8 @@
"Свернуть %1$s"
"%1$s: настройки"
"Скрыть всплывающий чат"
+
+
"Не показывать всплывающий чат для разговора"
"Всплывающие чаты"
"Новые разговоры будут появляться в виде плавающих значков, или всплывающих чатов. Чтобы открыть чат, нажмите на него, а чтобы переместить – перетащите."
@@ -127,6 +129,5 @@
"Открыть меню"
"Развернуть на весь экран"
"Свернуть"
-
-
+ "Изменить размер приложения нельзя."
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 1b70ffce6ae0e07441f0a31aa27bc603ca314efe..da2541e2ec140e2cc6a979dfa8b289ab87f47171 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -73,6 +73,8 @@
"%1$s හකුළන්න"
"%1$s සැකසීම්"
"බුබුලු ඉවත ලන්න"
+
+
"සංවාදය බුබුලු නොදමන්න"
"බුබුලු භාවිතයෙන් කතාබහ කරන්න"
"නව සංවාද පාවෙන අයිකන හෝ බුබුලු ලෙස දිස් වේ. බුබුල විවෘත කිරීමට තට්ටු කරන්න. එය ගෙන යාමට අදින්න."
@@ -127,6 +129,5 @@
"මෙනුව විවෘත කරන්න"
"තිරය උපරිම කරන්න"
"ස්නැප් තිරය"
-
-
+ "මෙම යෙදුම ප්රතිප්රමාණ කළ නොහැක"
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 344e3c7dd9777a396aa1b5a8e6231166041049ce..394a4cad292b33fb476bb342097133d22f2c242e 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -73,6 +73,8 @@
"zbaliť %1$s"
"Nastavenia aplikácie %1$s"
"Zavrieť bublinu"
+
+
"Nezobrazovať konverzáciu ako bublinu"
"Čet pomocou bublín"
"Nové konverzácie sa zobrazujú ako plávajúce ikony či bubliny. Bublinu otvoríte klepnutím. Premiestnite ju presunutím."
@@ -127,6 +129,5 @@
"Otvoriť ponuku"
"Maximalizovať obrazovku"
"Zobraziť polovicu obrazovky"
-
-
+ "Veľkosť tejto aplikácie sa nedá zmeniť"
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 118831d703d8adad2595eae76f9d4d72bf2ffc89..a90c2c216ef463004973b7f8d17923d19c7a68ef 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -73,6 +73,8 @@
"strnitev oblačka %1$s"
"Nastavitve za %1$s"
"Opusti oblaček"
+
+
"Pogovora ne prikaži v oblačku"
"Klepet z oblački"
"Novi pogovori so prikazani kot lebdeče ikone ali oblački. Če želite odpreti oblaček, se ga dotaknite. Če ga želite premakniti, ga povlecite."
@@ -127,6 +129,5 @@
"Odpri meni"
"Maksimiraj zaslon"
"Pripni zaslon"
-
-
+ "Velikosti te aplikacije ni mogoče spremeniti"
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 7976af0d5cddcbc6c9ae0bbeb5563bda0c4db1eb..706e75fc53aab993ebf92271777d019fa90413b5 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -73,6 +73,8 @@
"palos %1$s"
"Cilësimet e %1$s"
"Hiqe flluskën"
+
+
"Mos e vendos bisedën në flluskë"
"Bisedo duke përdorur flluskat"
"Bisedat e reja shfaqen si ikona pluskuese ose flluska. Trokit për të hapur flluskën. Zvarrit për ta zhvendosur."
@@ -127,6 +129,5 @@
"Hap menynë"
"Maksimizo ekranin"
"Regjistro ekranin"
-
-
+ "Përmasat e këtij aplikacioni nuk mund të ndryshohen"
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 6243c52cc03e659d9e82719194f6916d46fa9fcf..539a00a2034666056a2784258f151529841b2630 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -73,6 +73,8 @@
"скупите облачић %1$s"
"Подешавања за %1$s"
"Одбаци облачић"
+
+
"Не користи облачиће за конверзацију"
"Ћаскајте у облачићима"
"Нове конверзације се приказују као плутајуће иконе или облачићи. Додирните да бисте отворили облачић. Превуците да бисте га преместили."
@@ -127,6 +129,5 @@
"Отворите мени"
"Повећај екран"
"Уклопи екран"
-
-
+ "Величина ове апликације не може да се промени"
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 1584a3daa41b6904e43bfcde0d201e5de09096bb..549eb109f66279212244b2a0d667ebe93ae3f169 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -73,6 +73,8 @@
"komprimera %1$s"
"Inställningar för %1$s"
"Stäng bubbla"
+
+
"Visa inte konversationen i bubblor"
"Chatta med bubblor"
"Nya konversationer visas som flytande ikoner, så kallade bubblor. Tryck på bubblan om du vill öppna den. Dra den om du vill flytta den."
@@ -127,6 +129,5 @@
"Öppna menyn"
"Maximera skärmen"
"Fäst skärmen"
-
-
+ "Det går inte att ändra storlek på appen"
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index bf6af0c203cabe87877ec6bddc9274360ee217b9..9c3469081706acee43b3c1382ac457f4334fbf3f 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -73,6 +73,8 @@
"kunja %1$s"
"Mipangilio ya %1$s"
"Ondoa kiputo"
+
+
"Usiweke viputo kwenye mazungumzo"
"Piga gumzo ukitumia viputo"
"Mazungumzo mapya huonekena kama aikoni au viputo vinavyoelea. Gusa ili ufungue kiputo. Buruta ili ukisogeze."
@@ -127,6 +129,5 @@
"Fungua Menyu"
"Panua Dirisha kwenye Skrini"
"Panga Madirisha kwenye Skrini"
-
-
+ "Huwezi kubadilisha ukubwa wa programu hii"
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 825fc8f6b24ebb830df8d557424eca71193b0316..7e996f332bc2f22c868348041ef0e1d9e27233d2 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -73,6 +73,8 @@
"%1$s ஐச் சுருக்கும்"
"%1$s அமைப்புகள்"
"குமிழை அகற்று"
+
+
"உரையாடலைக் குமிழாக்காதே"
"குமிழ்களைப் பயன்படுத்தி அரட்டையடியுங்கள்"
"புதிய உரையாடல்கள் மிதக்கும் ஐகான்களாகவோ குமிழ்களாகவோ தோன்றும். குமிழைத் திறக்க தட்டவும். நகர்த்த இழுக்கவும்."
@@ -127,6 +129,5 @@
"மெனுவைத் திற"
"திரையைப் பெரிதாக்கு"
"திரையை ஸ்னாப் செய்"
-
-
+ "இந்த ஆப்ஸின் அளவை மாற்ற முடியாது"
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 17f33225b36901e1b740f8179a6f2fb2cf079db2..984cea87bbf045e4a75d2ac92d4252433767a7a4 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -73,6 +73,8 @@
"%1$sను కుదించండి"
"%1$s సెట్టింగ్లు"
"బబుల్ను విస్మరించు"
+
+
"సంభాషణను బబుల్ చేయవద్దు"
"బబుల్స్ను ఉపయోగించి చాట్ చేయండి"
"కొత్త సంభాషణలు తేలియాడే చిహ్నాలుగా లేదా బబుల్స్ లాగా కనిపిస్తాయి. బబుల్ని తెరవడానికి నొక్కండి. తరలించడానికి లాగండి."
@@ -127,6 +129,5 @@
"మెనూను తెరవండి"
"స్క్రీన్ సైజ్ను పెంచండి"
"స్క్రీన్ను స్నాప్ చేయండి"
-
-
+ "ఈ యాప్ సైజ్ను మార్చడం సాధ్యపడదు"
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index c03600f386022cf534ffdcc23b2da4d2df569bba..3460b0768ad0653bc7fa185634b6fca01b9c9dbf 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -73,6 +73,8 @@
"ยุบ %1$s"
"การตั้งค่า %1$s"
"ปิดบับเบิล"
+
+
"ไม่ต้องแสดงการสนทนาเป็นบับเบิล"
"แชทโดยใช้บับเบิล"
"การสนทนาใหม่ๆ จะปรากฏเป็นไอคอนแบบลอยหรือบับเบิล แตะเพื่อเปิดบับเบิล ลากเพื่อย้ายที่"
@@ -127,6 +129,5 @@
"เปิดเมนู"
"ขยายหน้าจอให้ใหญ่สุด"
"สแนปหน้าจอ"
-
-
+ "ปรับขนาดแอปนี้ไม่ได้"
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 174c5245a79d75424330c379ceaafa4a3b053855..dc309129ef2e486bae3e0bc7b15288c17705c559 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -73,6 +73,8 @@
"i-collapse ang %1$s"
"Mga setting ng %1$s"
"I-dismiss ang bubble"
+
+
"Huwag ipakita sa bubble ang mga pag-uusap"
"Mag-chat gamit ang bubbles"
"Lumalabas bilang mga nakalutang na icon o bubble ang mga bagong pag-uusap. I-tap para buksan ang bubble. I-drag para ilipat ito."
@@ -127,6 +129,5 @@
"Buksan ang Menu"
"I-maximize ang Screen"
"I-snap ang Screen"
-
-
+ "Hindi nare-resize ang app na ito"
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 0ae2a6aff5d40ffeed71c5adae11822121bfde92..e9e21739cb610c465047db7ad23a4c87cc6a9bf0 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -73,6 +73,8 @@
"daralt: %1$s"
"%1$s ayarları"
"Baloncuğu kapat"
+
+
"Görüşmeyi baloncuk olarak görüntüleme"
"Baloncukları kullanarak sohbet edin"
"Yeni görüşmeler kayan simgeler veya baloncuk olarak görünür. Açmak için baloncuğa dokunun. Baloncuğu taşımak için sürükleyin."
@@ -127,6 +129,5 @@
"Menüyü Aç"
"Ekranı Büyüt"
"Ekranın Yarısına Tuttur"
-
-
+ "Bu uygulama yeniden boyutlandırılamaz"
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 64ca28c45d6962937c2827071c8262733de78fe9..e1b6e3505c0435d11ca3bcf9e501e5c984be6583 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -73,6 +73,8 @@
"згорнути \"%1$s\""
"Налаштування параметра \"%1$s\""
"Закрити підказку"
+
+
"Не показувати спливаючі чати для розмов"
"Спливаючий чат"
"Нові повідомлення чату з\'являються у вигляді спливаючих значків. Щоб відкрити чат, натисніть його, а щоб перемістити – перетягніть."
@@ -127,6 +129,5 @@
"Відкрити меню"
"Розгорнути екран"
"Зафіксувати екран"
-
-
+ "Розмір вікна цього додатка не можна змінити"
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 19cef43ce8fd473c09f870c79c288738decb93d1..0508e6d9a7e3b6e2b8ce7f043fede1afacd48a2d 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -73,6 +73,8 @@
"%1$s کو سکیڑیں"
"%1$s ترتیبات"
"بلبلہ برخاست کریں"
+
+
"گفتگو بلبلہ نہ کریں"
"بلبلے کے ذریعے چیٹ کریں"
"نئی گفتگوئیں فلوٹنگ آئیکن یا بلبلے کے طور پر ظاہر ہوں گی۔ بلبلہ کھولنے کے لیے تھپتھپائیں۔ اسے منتقل کرنے کے لیے گھسیٹیں۔"
@@ -127,6 +129,5 @@
"مینو کھولیں"
"اسکرین کو بڑا کریں"
"اسکرین کا اسناپ شاٹ لیں"
-
-
+ "اس ایپ کا سائز تبدیل نہیں کیا جا سکتا"
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 6768e07c276a236dd6df8d576b48cf2d545fda78..5de3b7bf52aa89a2d2dbb90a929b7b171f36548b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -73,6 +73,8 @@
"%1$sni yopish"
"%1$s sozlamalari"
"Bulutchani yopish"
+
+
"Suhbatlar bulutchalar shaklida chiqmasin"
"Bulutchalar yordamida subhatlashish"
"Yangi xabarlar qalqib chiquvchi belgilar yoki bulutchalar kabi chiqadi. Xabarni ochish uchun bildirishnoma ustiga bosing. Xabarni qayta joylash uchun bildirishnomani suring."
@@ -127,6 +129,5 @@
"Menyuni ochish"
"Ekranni yoyish"
"Ekranni biriktirish"
-
-
+ "Bu ilova hajmini oʻzgartirish imkonsiz"
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index eef1e8ec2dd9850121dcf00c4f2c0633b906c067..9b2f8988fcca6b0cd25ab14c82a938349806e031 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -73,6 +73,8 @@
"thu gọn %1$s"
"Cài đặt %1$s"
"Đóng bong bóng"
+
+
"Dừng sử dụng bong bóng cho cuộc trò chuyện"
"Trò chuyện bằng bong bóng trò chuyện"
"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng nổi hoặc bong bóng trò chuyện. Nhấn để mở bong bóng trò chuyện. Kéo để di chuyển bong bóng trò chuyện."
@@ -127,6 +129,5 @@
"Mở Trình đơn"
"Mở rộng màn hình"
"Điều chỉnh kích thước màn hình"
-
-
+ "Không thể đổi kích thước của ứng dụng này"
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index b152c0aa9e3422c831d48b3459abfb991d0e9455..b45b76e876f2679b5fd9a7364d31f934118a4c94 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -73,6 +73,8 @@
"收起“%1$s”"
"%1$s设置"
"关闭消息气泡"
+
+
"不以消息气泡形式显示对话"
"使用消息气泡聊天"
"新对话会以浮动图标或消息气泡形式显示。点按即可打开消息气泡。拖动即可移动消息气泡。"
@@ -127,6 +129,5 @@
"打开菜单"
"最大化屏幕"
"屏幕快照"
-
-
+ "无法调整此应用的大小"
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index d96739fe8d956389b80cacded5e70166ad1233f6..ae776b8c9b125eb5f6330f587729d1cf972dbd53 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -73,6 +73,8 @@
"收埋%1$s"
"「%1$s」設定"
"關閉小視窗氣泡"
+
+
"不要透過小視窗顯示對話"
"使用小視窗進行即時通訊"
"新對話會以浮動圖示 (小視窗) 顯示。輕按即可開啟小視窗。拖曳即可移動小視窗。"
@@ -127,6 +129,5 @@
"打開選單"
"畫面最大化"
"貼齊畫面"
-
-
+ "此應用程式無法調整大小"
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 6bc93e6cbd49a02f7679c8eaff7892fae00366a5..5bfc6b843aad429b8fb23ae40f40848be26e8268 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -73,6 +73,8 @@
"收合「%1$s」"
"「%1$s」設定"
"關閉對話框"
+
+
"不要以對話框形式顯示對話"
"透過對話框來聊天"
"新的對話會以浮動圖示或對話框形式顯示。輕觸即可開啟對話框,拖曳則可移動對話框。"
@@ -127,6 +129,5 @@
"開啟選單"
"畫面最大化"
"貼齊畫面"
-
-
+ "這個應用程式無法調整大小"
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index e152705b240b4dbc35e2938a6670b538a7cb6a27..0598d62dcbb6add00fab90930bfbdda66ecacfeb 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -73,6 +73,8 @@
"goqa %1$s"
"%1$s izilungiselelo"
"Cashisa ibhamuza"
+
+
"Ungayibhamuzi ingxoxo"
"Xoxa usebenzisa amabhamuza"
"Izingxoxo ezintsha zivela njengezithonjana ezintantayo, noma amabhamuza. Thepha ukuze uvule ibhamuza. Hudula ukuze ulihambise."
@@ -127,6 +129,5 @@
"Vula Imenyu"
"Khulisa Isikrini Sifike Ekugcineni"
"Thwebula Isikrini"
-
-
+ "Le app ayikwazi ukushintshwa usayizi"
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 36d0a3c63b034a03b25d857c598a0269abeccc77..a353db72b9140354ccc9ead73413984bc23a4dda 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -155,6 +155,8 @@
%1$s settings
Dismiss bubble
+
+ Move to fullscreen
Don\u2019t bubble conversation
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
index eec24683db8a0e65acd55e75d63af7d4cf81932a..7086691e7431c4f92649a42864af7b4e80ab9d5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.graphics.Point
import android.graphics.RectF
@@ -23,9 +23,9 @@ import androidx.annotation.VisibleForTesting
import androidx.core.animation.Animator
import androidx.core.animation.AnimatorListenerAdapter
import androidx.core.animation.ObjectAnimator
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
/**
* Base class for common logic shared between different bubble views to support pinning bubble bar
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
similarity index 93%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
index 3c5beeb48806fb94d79d4432d9924bd0800b88d1..4fe76115fa0b13497cefe1329932940c90cec36e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
parcelable BubbleBarLocation;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
index f0bdfdef1073031b67bff794250876f8e28d1cb0..191875d38dafbde4f487a548c4246db709e67763 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.os.Parcel
import android.os.Parcelable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
index ec3c6013e54453fb7be6413fecbcdc8fe60225e0..5bde1e8fae3bac1c9b72a906486ba4bd0d25bcac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
similarity index 89%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
index 0329b8df7544bcbbb62a3256d2e3b5bfda21272c..3396bc441467bfefd2719946fe40b0fa4b8a59ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
/**
* Constants shared between bubbles in shell & things we have to do for bubbles in launcher.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
similarity index 92%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
index 829af08e612a03f485f56cf1da6740f816059bb7..58766826bd3b95b962a076741a67fa3d25cf16e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,10 +48,11 @@ public class BubbleInfo implements Parcelable {
@Nullable
private String mAppName;
private boolean mIsImportantConversation;
+ private boolean mShowAppBadge;
public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
int userId, String packageName, @Nullable String title, @Nullable String appName,
- boolean isImportantConversation) {
+ boolean isImportantConversation, boolean showAppBadge) {
mKey = key;
mFlags = flags;
mShortcutId = shortcutId;
@@ -61,6 +62,7 @@ public class BubbleInfo implements Parcelable {
mTitle = title;
mAppName = appName;
mIsImportantConversation = isImportantConversation;
+ mShowAppBadge = showAppBadge;
}
private BubbleInfo(Parcel source) {
@@ -73,6 +75,7 @@ public class BubbleInfo implements Parcelable {
mTitle = source.readString();
mAppName = source.readString();
mIsImportantConversation = source.readBoolean();
+ mShowAppBadge = source.readBoolean();
}
public String getKey() {
@@ -115,6 +118,10 @@ public class BubbleInfo implements Parcelable {
return mIsImportantConversation;
}
+ public boolean showAppBadge() {
+ return mShowAppBadge;
+ }
+
/**
* Whether this bubble is currently being hidden from the stack.
*/
@@ -172,6 +179,7 @@ public class BubbleInfo implements Parcelable {
parcel.writeString(mTitle);
parcel.writeString(mAppName);
parcel.writeBoolean(mIsImportantConversation);
+ parcel.writeBoolean(mShowAppBadge);
}
@NonNull
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
index 887af17c96539c578b8657a7940b72b33f650722..8681acf93ab3631c199647440ecb2845db8a5d06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.annotation.ColorInt
import android.graphics.Canvas
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
similarity index 95%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
index 444fbf7884beec75944aeeb586b7f7054a8eaf2f..802d7d131d95b422a6acb110c5f4c648e648a8fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.content.Context
import android.graphics.Rect
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
index 7c5bb211a4ccd4cc1f046cbb84926d0ccfc3299f..0c051560f71430416824652c8d8bfb5786e2399a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.content.Context;
import android.content.res.Configuration;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
index e06de9e9353c6dd0b137dc5b1970aaf1db394ed8..2bb66b0bbcd365cf244c92981bcdd8103f5b7e49 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.animation.ObjectAnimator
import android.content.Context
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
similarity index 99%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
index 4e55ba23407b75726e5d7d46ee34debe6c3422eb..b1f4e331a98ddd405416bb21c07fe4f95d0d8047 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.graphics.PointF
import android.view.MotionEvent
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
similarity index 95%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
index f90591b84b7eb35dd7567cecec016fcf8f31112f..c83696c0161377beb4d6a2e3f3dde5fcd8a0796a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.os.Parcel;
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
index 47d52744cd7494569cab2d40270e5405509478d5..424d4bf5c6e8a83f6b3d8d9ea45ddf1cd6518862 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -48,7 +48,7 @@ enum class DesktopModeFlags(
TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
DISABLE_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
- DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true),
+ DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false),
ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
similarity index 98%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
index 9999f08ee03c2e63679729b8bf2c6f3570a35279..a06cf78d0898019f9cc9faf0cf83a482a4f562d9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.navigationbar;
+package com.android.wm.shell.shared.handles;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -194,7 +194,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl();
SurfaceControl stopLayerControl = null;
if (viewRootImpl != null) {
- stopLayerControl = viewRootImpl.getSurfaceControl();
+ stopLayerControl = viewRootImpl.getSurfaceControl();
}
if (stopLayerControl == null || !stopLayerControl.isValid()) {
if (!mWaitingOnDraw) {
@@ -329,7 +329,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
/**
* Get the sampled region of interest from the sampled view
* @param sampledView The view that this helper is attached to for convenience
- * @return the region to be sampled in sceen coordinates. Return {@code null} to avoid
+ * @return the region to be sampled in screen coordinates. Return {@code null} to avoid
* sampling in this frame
*/
Rect getSampledRegion(View sampledView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..05ce36120c4f8c1972fc19c55518acf52724bdee
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("AppToWebUtils")
+
+package com.android.wm.shell.apptoweb
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+
+private val browserIntent = Intent()
+ .setAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse("http:"))
+
+/**
+ * Returns a boolean indicating whether a given package is a browser app.
+ */
+fun isBrowserApp(context: Context, packageName: String, userId: Int): Boolean {
+ browserIntent.setPackage(packageName)
+ val list = context.packageManager.queryIntentActivitiesAsUser(
+ browserIntent, PackageManager.MATCH_ALL, userId
+ )
+
+ list.forEach {
+ if (it.activityInfo != null && it.handleAllWebDataURI) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt
new file mode 100644
index 0000000000000000000000000000000000000000..249185eca323010070aff8a9f192460d7a516b1a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.apptoweb
+
+import android.app.ActivityTaskManager
+import android.app.IActivityTaskManager
+import android.app.IAssistDataReceiver
+import android.app.assist.AssistContent
+import android.content.Context
+import android.graphics.Bitmap
+import android.os.Bundle
+import android.os.RemoteException
+import android.util.Slog
+import java.lang.ref.WeakReference
+import java.util.Collections
+import java.util.WeakHashMap
+import java.util.concurrent.Executor
+
+/**
+ * Can be used to request the AssistContent from a provided task id, useful for getting the web uri
+ * if provided from the task.
+ */
+class AssistContentRequester(
+ context: Context,
+ private val callBackExecutor: Executor,
+ private val systemInteractionExecutor: Executor
+) {
+ interface Callback {
+ // Called when the [AssistContent] of the requested task is available.
+ fun onAssistContentAvailable(assistContent: AssistContent?)
+ }
+
+ private val activityTaskManager: IActivityTaskManager = ActivityTaskManager.getService()
+ private val attributionTag: String? = context.attributionTag
+ private val packageName: String = context.applicationContext.packageName
+
+ // If system loses the callback, our internal cache of original callback will also get cleared.
+ private val pendingCallbacks = Collections.synchronizedMap(WeakHashMap())
+
+ /**
+ * Request the [AssistContent] from the task with the provided id.
+ *
+ * @param taskId to query for the content.
+ * @param callback to call when the content is available, called on the main thread.
+ */
+ fun requestAssistContent(taskId: Int, callback: Callback) {
+ // ActivityTaskManager interaction here is synchronous, so call off the main thread.
+ systemInteractionExecutor.execute {
+ try {
+ val success = activityTaskManager.requestAssistDataForTask(
+ AssistDataReceiver(callback, this),
+ taskId,
+ packageName,
+ attributionTag,
+ false /* fetchStructure */
+ )
+ if (!success) {
+ executeOnMainExecutor { callback.onAssistContentAvailable(null) }
+ }
+ } catch (e: RemoteException) {
+ Slog.e(TAG, "Requesting assist content failed for task: $taskId", e)
+ }
+ }
+ }
+
+ private fun executeOnMainExecutor(callback: Runnable) {
+ callBackExecutor.execute(callback)
+ }
+
+ private class AssistDataReceiver(
+ callback: Callback,
+ parent: AssistContentRequester
+ ) : IAssistDataReceiver.Stub() {
+ // The AssistDataReceiver binder callback object is passed to a system server, that may
+ // keep hold of it for longer than the lifetime of the AssistContentRequester object,
+ // potentially causing a memory leak. In the callback passed to the system server, only
+ // keep a weak reference to the parent object and lookup its callback if it still exists.
+ private val parentRef: WeakReference
+ private val callbackKey = Any()
+
+ init {
+ parent.pendingCallbacks[callbackKey] = callback
+ parentRef = WeakReference(parent)
+ }
+
+ override fun onHandleAssistData(data: Bundle?) {
+ val content = data?.getParcelable(ASSIST_KEY_CONTENT, AssistContent::class.java)
+ if (content == null) {
+ Slog.d(TAG, "Received AssistData, but no AssistContent found")
+ return
+ }
+ val requester = parentRef.get()
+ if (requester != null) {
+ val callback = requester.pendingCallbacks[callbackKey]
+ if (callback != null) {
+ requester.executeOnMainExecutor { callback.onAssistContentAvailable(content) }
+ } else {
+ Slog.d(TAG, "Callback received after calling UI was disposed of")
+ }
+ } else {
+ Slog.d(TAG, "Callback received after Requester was collected")
+ }
+ }
+
+ override fun onHandleAssistScreenshot(screenshot: Bitmap) {}
+ }
+
+ companion object {
+ private const val TAG = "AssistContentRequester"
+ private const val ASSIST_KEY_CONTENT = "content"
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
index bfe1306a60e654ae2a6a91002745ae8b45810612..6207e5b020f79e525849eae7825ddd3628ad60f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
@@ -1,6 +1,8 @@
atsjenk@google.com
jorgegil@google.com
madym@google.com
+mattsziklay@google.com
+mdehaini@google.com
pbdr@google.com
tkachenkoi@google.com
vaniadesmonda@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 27194b344780017543359f898778a3854b7269e0..7b3b2071ef02b58c05d2c79f88850f2d3ee23590 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -74,6 +74,7 @@ import android.window.IBackAnimationRunner;
import android.window.IOnBackInvokedCallback;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
@@ -572,8 +573,14 @@ public class BackAnimationController implements RemoteCallable= 0; --j) {
final TransitionInfo.Change change = init.getChanges().get(j);
if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
openComponent = findComponentName(change);
openTaskId = findTaskId(change);
+ openToken = findToken(change);
if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
openShowWallpaper = true;
}
break;
}
}
- if (openComponent == null && openTaskId == INVALID_TASK_ID) {
- // shouldn't happen.
+ if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) {
+ // This shouldn't happen, but if that happen, consume the initial transition anyway.
+ Log.e(TAG, "Unable to merge following transition, cannot find the gesture "
+ + "animated target from the open transition=" + mOpenTransitionInfo);
+ mOpenTransitionInfo = null;
return;
}
// find first non-prepare open target
@@ -1315,7 +1329,7 @@ public class BackAnimationController implements RemoteCallable= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isSameChangeTarget(openComponent, openTaskId, change)) {
+ if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))
@@ -1328,8 +1342,11 @@ public class BackAnimationController implements RemoteCallable= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isSameChangeTarget(openComponent, openTaskId, change)) {
+ if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
info.getChanges().remove(j);
@@ -1368,6 +1385,8 @@ public class BackAnimationController implements RemoteCallable= 0; --i) {
final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.hasFlags(FLAG_IS_WALLPAPER)) {
+ st.setAlpha(c.getLeash(), 1.0f);
+ continue;
+ }
if (TransitionUtil.isOpeningMode(c.getMode())) {
final Point offset = c.getEndRelOffset();
st.setPosition(c.getLeash(), offset.x, offset.y);
st.reparent(c.getLeash(), openingLeash);
st.setAlpha(c.getLeash(), 1.0f);
+ rootIdx = TransitionUtil.rootIndexFor(c, info);
}
}
+ // The root leash and the leash of opening target should actually in the same level,
+ // but since the root leash is created after opening target, it will have higher
+ // layer in surface flinger. Move the root leash to lower level, so it won't affect
+ // the playing animation.
+ if (rootIdx >= 0 && info.getRootCount() > 0) {
+ st.setLayer(info.getRoot(rootIdx).getLeash(), -1);
+ }
}
st.apply();
mFinishOpenTransaction = ft;
@@ -1537,6 +1579,10 @@ public class BackAnimationController implements RemoteCallable= 0; --i) {
final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.hasFlags(FLAG_IS_WALLPAPER)) {
+ st.setAlpha(c.getLeash(), 1.0f);
+ continue;
+ }
if (TransitionUtil.isOpeningMode(c.getMode())) {
final Point offset = c.getEndRelOffset();
st.setPosition(c.getLeash(), offset.x, offset.y);
@@ -1627,6 +1673,10 @@ public class BackAnimationController implements RemoteCallable mSupportedAnimators = new ArrayList<>();
public ShellBackAnimationRegistry(
@ShellBackAnimation.CrossActivity @Nullable ShellBackAnimation crossActivityAnimation,
@@ -60,7 +64,7 @@ public class ShellBackAnimationRegistry {
mDefaultCrossActivityAnimation = crossActivityAnimation;
mCustomizeActivityAnimation = customizeActivityAnimation;
mCrossTaskAnimation = crossTaskAnimation;
-
+ updateSupportedAnimators();
// TODO(b/236760237): register dialog close animation when it's completed.
}
@@ -71,6 +75,7 @@ public class ShellBackAnimationRegistry {
if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
mDefaultCrossActivityAnimation = null;
}
+ updateSupportedAnimators();
}
void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
@@ -79,6 +84,24 @@ public class ShellBackAnimationRegistry {
if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
mDefaultCrossActivityAnimation = null;
}
+ updateSupportedAnimators();
+ }
+
+ private void updateSupportedAnimators() {
+ mSupportedAnimators.clear();
+ for (int i = mAnimationDefinition.size() - 1; i >= 0; --i) {
+ mSupportedAnimators.add(mAnimationDefinition.keyAt(i));
+ }
+ mSupportedAnimatorsChanged = true;
+ }
+
+ boolean hasSupportedAnimatorsChanged() {
+ return mSupportedAnimatorsChanged;
+ }
+
+ ArrayList getSupportedAnimators() {
+ mSupportedAnimatorsChanged = false;
+ return mSupportedAnimators;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 3e758bbad29b95bbd3f43a08e9d1008b6d4c82ef..169361ad5f6b62b1b47a7a63dc61716181387c88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -53,9 +53,9 @@ import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
import java.io.PrintWriter;
import java.util.List;
@@ -349,7 +349,8 @@ public class Bubble implements BubbleViewProvider {
getPackageName(),
getTitle(),
getAppName(),
- isImportantConversation());
+ isImportantConversation(),
+ !isAppLaunchIntent());
}
@Override
@@ -608,7 +609,8 @@ public class Bubble implements BubbleViewProvider {
callback.onBubbleViewsReady(bubble);
}
},
- mMainExecutor);
+ mMainExecutor,
+ mBgExecutor);
if (mInflateSynchronously) {
mInflationTaskLegacy.onPostExecute(mInflationTaskLegacy.doInBackground());
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index b508c1ba7fe4efabc7d4c57ccc2af31438a01df3..c545d73734f0065c473c6c10e6dea2f7e55bfb9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -104,14 +104,14 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
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 4ad1802cba7fb95b84092813f42fd55f27b765f9..709a7bdc61f20dc975661fa241494188f7632c32 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
@@ -41,10 +41,10 @@ import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubbles.DismissReason;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
-import com.android.wm.shell.common.bubbles.RemovedBubble;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.RemovedBubble;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index 4e80e903b522b1bf577caa455d42283f2cd44b96..ec4854b47aff39af4b7e647aaca798fc9baed938 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.bubbles
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/** Manager interface for bubble expanded views. */
interface BubbleExpandedViewManager {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index f32974e1765de42ad4a554c2501ea71f8b250878..68c4657f2b6883eb165ba38781c8c4a4ac45f1d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -80,7 +80,10 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl
expandedViewManager,
positioner,
/* isOverflow= */ true,
- /* bubbleTaskView= */ null
+ /* bubbleTaskView= */ null,
+ /* mainExecutor= */ null,
+ /* backgroundExecutor= */ null,
+ /* regionSamplingProvider= */ null
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
index bdb09e11d5ad911462c7dfd99d977258c328482c..fd110a2768266bceb2c254768ca0fdccbc2ccf58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
@@ -17,8 +17,8 @@ package com.android.wm.shell.bubbles
import android.graphics.Color
import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.BubblePopupDrawable
-import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
+import com.android.wm.shell.shared.bubbles.BubblePopupView
/**
* A convenience method to setup the [BubblePopupView] with the correct config using local resources
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 0cf187bd9c0ffe71c197a9084392cfbf6e7f5363..c386c9398624736c04c9d4a9e2af5087d03922af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -32,7 +32,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Keeps track of display size, configuration, and specific bubble sizes. One place for all
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 53bbf888df5a8960c215b99d2929fac296d1ca05..2795881f093814434ddbfe8b9204eab3e97e65d8 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
@@ -24,10 +24,10 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.LEFT;
import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.RIGHT;
-import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -91,8 +91,8 @@ import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.bubbles.RelativeTouchListener;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 5f8f0fd0c54c7a87299608a3fb93edd45b94eadd..0c0fd7b10f6e5592eb7cd9e5efa86c66f9c005eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -60,6 +60,9 @@ public class BubbleTaskViewHelper {
/** Called when back is pressed on the task root. */
void onBackPressed();
+
+ /** Called when task removal has started. */
+ void onTaskRemovalStarted();
}
private final Context mContext;
@@ -190,6 +193,7 @@ public class BubbleTaskViewHelper {
((ViewGroup) mParentView).removeView(mTaskView);
mTaskView = null;
}
+ mListener.onTaskRemovalStarted();
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 13855f73fb4ae1741a0a8df23f7413fe7f3b7854..3982a237dd3b6e01849a37bc0a64060eee7cdb8f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -38,6 +38,7 @@ import android.graphics.drawable.Icon;
import android.util.Log;
import android.util.PathParser;
import android.view.LayoutInflater;
+import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -47,6 +48,7 @@ import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -222,7 +224,16 @@ public class BubbleViewInfoTask {
ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s",
mBubble.getKey());
viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(),
- mPositioner.get(), false /* isOverflow */, viewInfo.taskView);
+ mPositioner.get(), false /* isOverflow */, viewInfo.taskView,
+ mMainExecutor, mBgExecutor, new RegionSamplingProvider() {
+ @Override
+ public RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor, Executor mainExecutor) {
+ return RegionSamplingProvider.super.createHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+ });
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
index 5cfebf8f164791edcb0f7b8afa1fd094447cba41..1b7bb0db65163c53adc63fbe0d8752be0fb4c1e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -38,6 +38,7 @@ import android.os.AsyncTask;
import android.util.Log;
import android.util.PathParser;
import android.view.LayoutInflater;
+import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -46,6 +47,7 @@ import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -85,6 +87,7 @@ public class BubbleViewInfoTaskLegacy extends
private boolean mSkipInflation;
private Callback mCallback;
private Executor mMainExecutor;
+ private Executor mBackgroundExecutor;
/**
* Creates a task to load information for the provided {@link Bubble}. Once all info
@@ -100,7 +103,8 @@ public class BubbleViewInfoTaskLegacy extends
BubbleIconFactory factory,
boolean skipInflation,
Callback c,
- Executor mainExecutor) {
+ Executor mainExecutor,
+ Executor backgroundExecutor) {
mBubble = b;
mContext = new WeakReference<>(context);
mExpandedViewManager = new WeakReference<>(expandedViewManager);
@@ -112,6 +116,7 @@ public class BubbleViewInfoTaskLegacy extends
mSkipInflation = skipInflation;
mCallback = c;
mMainExecutor = mainExecutor;
+ mBackgroundExecutor = backgroundExecutor;
}
@Override
@@ -123,7 +128,7 @@ public class BubbleViewInfoTaskLegacy extends
if (mLayerView.get() != null) {
return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
- mBubble, mSkipInflation);
+ mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor);
} else {
return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
@@ -188,7 +193,9 @@ public class BubbleViewInfoTaskLegacy extends
BubbleBarLayerView layerView,
BubbleIconFactory iconFactory,
Bubble b,
- boolean skipInflation) {
+ boolean skipInflation,
+ Executor mainExecutor,
+ Executor backgroundExecutor) {
BubbleViewInfo info = new BubbleViewInfo();
if (!skipInflation && !b.isInflated()) {
@@ -197,7 +204,16 @@ public class BubbleViewInfoTaskLegacy extends
info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
info.bubbleBarExpandedView.initialize(
- expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView);
+ expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView,
+ mainExecutor, backgroundExecutor, new RegionSamplingProvider() {
+ @Override
+ public RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor, Executor mainExecutor) {
+ return RegionSamplingProvider.super.createHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+ });
}
if (!populateCommonInfo(info, c, b, iconFactory)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 9a27fb65ac2c7f5cc314633d076680b8ccf6b997..62895fe7c7cc384961837397131b5dfca3aca909 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -38,9 +38,9 @@ import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
index 48692d41016eaff868098de6ab7a25eb089731e1..00a81727a9ac12121daa1146ac07dad6b4b531ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
@@ -18,7 +18,7 @@
package com.android.wm.shell.bubbles
import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.DismissView
fun DismissView.setup() {
setup(DismissView.Config(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 5779a8f7bcc47b7ec6d88ee4d078c17c2db3c0a9..1855b938f48e2d1c37884920586b8e61b37b141c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -20,7 +20,7 @@ import android.content.Intent;
import android.graphics.Rect;
import android.content.pm.ShortcutInfo;
import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Interface that is exposed to remote callers (launcher) to manipulate the bubbles feature when
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
index 14d29cd887bbc25cd8fbe6b24a8c09f5f168b8d8..eb907dbb6597675b5c6329b98d1a0b7708a6549d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
@@ -17,7 +17,7 @@
package com.android.wm.shell.bubbles;
import android.os.Bundle;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Listener interface that Launcher attaches to SystemUI to get bubbles callbacks.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..30f5c8fd56c3f2c3b9436ee9e5f507886baa2bfa
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles;
+
+import android.view.View;
+
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Wrapper to provide a {@link com.android.wm.shell.shared.handles.RegionSamplingHelper} to allow
+ * testing it.
+ */
+public interface RegionSamplingProvider {
+
+ /** Creates and returns the region sampling helper */
+ default RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor,
+ Executor mainExecutor) {
+ return new RegionSamplingHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 565fde0a853c0de8456994a4f5f9f26d5f5faf86..74c3748dccafdc5052b4cd63c453c9aecb50dfb1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -253,6 +253,7 @@ public class BubbleBarAnimationHelper {
return;
}
setDragPivot(bbev);
+ bbev.setDragging(true);
// Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius
final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_DRAG_SCALE;
@@ -329,6 +330,7 @@ public class BubbleBarAnimationHelper {
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
bbev.resetPivot();
+ bbev.setDragging(false);
}
});
startNewDragAnimation(animatorSet);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 6d868d21548244f796f5ee2af015c5cbc2d74eba..ec235a5d84ab6c349be19ed38f1809f2b22a6201 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -19,10 +19,7 @@ package com.android.wm.shell.bubbles.bar;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -37,6 +34,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
@@ -46,9 +44,12 @@ import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleTaskView;
import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.bubbles.RegionSamplingProvider;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import com.android.wm.shell.taskview.TaskView;
+import java.util.concurrent.Executor;
import java.util.function.Supplier;
/** Expanded view of a bubble when it's part of the bubble bar. */
@@ -92,16 +93,35 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
private boolean mIsOverflow;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
private BubbleBarMenuViewController mMenuViewController;
- private @Nullable Supplier mLayerBoundsSupplier;
- private @Nullable Listener mListener;
+ @Nullable
+ private Supplier mLayerBoundsSupplier;
+ @Nullable
+ private Listener mListener;
private BubbleBarHandleView mHandleView;
- private @Nullable TaskView mTaskView;
- private @Nullable BubbleOverflowContainerView mOverflowView;
+ @Nullable
+ private TaskView mTaskView;
+ @Nullable
+ private BubbleOverflowContainerView mOverflowView;
+ /**
+ * The handle shown in the caption area is tinted based on the background color of the area.
+ * This can vary so we sample the caption region and update the handle color based on that.
+ * If we're showing the overflow, the helper and executors will be null.
+ */
+ @Nullable
+ private RegionSamplingHelper mRegionSamplingHelper;
+ @Nullable
+ private RegionSamplingProvider mRegionSamplingProvider;
+ @Nullable
+ private Executor mMainExecutor;
+ @Nullable
+ private Executor mBackgroundExecutor;
+ private final Rect mSampleRect = new Rect();
+ private final int[] mLoc = new int[2];
+
+ /** Height of the caption inset at the top of the TaskView */
private int mCaptionHeight;
-
- private int mBackgroundColor;
/** Corner radius used when view is resting */
private float mRestingCornerRadius = 0f;
/** Corner radius applied while dragging */
@@ -116,6 +136,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
*/
private boolean mIsContentVisible = false;
private boolean mIsAnimating;
+ private boolean mIsDragging;
public BubbleBarExpandedView(Context context) {
this(context, null);
@@ -154,21 +175,20 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
setOnTouchListener((v, event) -> true);
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- // Hide manage menu when view disappears
- mMenuViewController.hideMenu(false /* animated */);
- }
-
/** Initializes the view, must be called before doing anything else. */
public void initialize(BubbleExpandedViewManager expandedViewManager,
BubblePositioner positioner,
boolean isOverflow,
- @Nullable BubbleTaskView bubbleTaskView) {
+ @Nullable BubbleTaskView bubbleTaskView,
+ @Nullable Executor mainExecutor,
+ @Nullable Executor backgroundExecutor,
+ @Nullable RegionSamplingProvider regionSamplingProvider) {
mManager = expandedViewManager;
mPositioner = positioner;
mIsOverflow = isOverflow;
+ mMainExecutor = mainExecutor;
+ mBackgroundExecutor = backgroundExecutor;
+ mRegionSamplingProvider = regionSamplingProvider;
if (mIsOverflow) {
mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
@@ -191,6 +211,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
mTaskView.setEnableSurfaceClipping(true);
mTaskView.setCornerRadius(mCurrentCornerRadius);
mTaskView.setVisibility(VISIBLE);
+ mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
// Handle view needs to draw on top of task view.
bringChildToFront(mHandleView);
@@ -228,6 +249,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
public void onDismissBubble(Bubble bubble) {
mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE);
}
+
+ @Override
+ public void onMoveToFullscreen(Bubble bubble) {
+ if (mTaskView != null) {
+ mTaskView.moveToFullscreen();
+ }
+ }
});
mHandleView.setOnClickListener(view -> {
mMenuViewController.showMenu(true /* animated */);
@@ -238,31 +266,39 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
return mHandleView;
}
- // TODO (b/275087636): call this when theme/config changes
/** Updates the view based on the current theme. */
public void applyThemeAttrs() {
+ mCaptionHeight = getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_caption_height);
mRestingCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_corner_radius
- );
+ R.dimen.bubble_bar_expanded_view_corner_radius);
mDraggedCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_corner_radius_dragged
- );
+ R.dimen.bubble_bar_expanded_view_corner_radius_dragged);
mCurrentCornerRadius = mRestingCornerRadius;
- final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- android.R.attr.colorBackgroundFloating});
- mBackgroundColor = ta.getColor(0, Color.WHITE);
- ta.recycle();
- mCaptionHeight = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_caption_height);
-
if (mTaskView != null) {
mTaskView.setCornerRadius(mCurrentCornerRadius);
- updateHandleColor(true /* animated */);
+ mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ // Hide manage menu when view disappears
+ mMenuViewController.hideMenu(false /* animated */);
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
}
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ recreateRegionSamplingHelper();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -277,16 +313,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mTaskView != null) {
- mTaskView.layout(l, t, r,
- t + mTaskView.getMeasuredHeight());
- mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
+ mTaskView.layout(l, t, r, t + mTaskView.getMeasuredHeight());
}
}
@Override
public void onTaskCreated() {
setContentVisibility(true);
- updateHandleColor(false /* animated */);
if (mListener != null) {
mListener.onTaskCreated();
}
@@ -297,12 +330,71 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
setContentVisibility(visible);
}
+ @Override
+ public void onTaskRemovalStarted() {
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+ }
+
@Override
public void onBackPressed() {
if (mListener == null) return;
mListener.onBackPressed();
}
+ /**
+ * Set whether this view is currently being dragged.
+ *
+ * When dragging, the handle is hidden and content shouldn't be sampled. When dragging has
+ * ended we should start again.
+ */
+ public void setDragging(boolean isDragging) {
+ if (isDragging != mIsDragging) {
+ mIsDragging = isDragging;
+ updateSamplingState();
+ }
+ }
+
+ /** Returns whether region sampling should be enabled, i.e. if task view content is visible. */
+ private boolean shouldSampleRegion() {
+ return mTaskView != null
+ && mTaskView.getTaskInfo() != null
+ && !mIsDragging
+ && !mIsAnimating
+ && mIsContentVisible;
+ }
+
+ /**
+ * Handles starting or stopping the region sampling helper based on
+ * {@link #shouldSampleRegion()}.
+ */
+ private void updateSamplingState() {
+ if (mRegionSamplingHelper == null) return;
+ boolean shouldSample = shouldSampleRegion();
+ if (shouldSample) {
+ mRegionSamplingHelper.start(getCaptionSampleRect());
+ } else {
+ mRegionSamplingHelper.stop();
+ }
+ }
+
+ /** Returns the current area of the caption bar, in screen coordinates. */
+ Rect getCaptionSampleRect() {
+ if (mTaskView == null) return null;
+ mTaskView.getLocationOnScreen(mLoc);
+ mSampleRect.set(mLoc[0], mLoc[1],
+ mLoc[0] + mTaskView.getWidth(),
+ mLoc[1] + mCaptionHeight);
+ return mSampleRect;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ public RegionSamplingHelper getRegionSamplingHelper() {
+ return mRegionSamplingHelper;
+ }
+
/** Cleans up the expanded view, should be called when the bubble is no longer active. */
public void cleanUpExpandedState() {
mMenuViewController.hideMenu(false /* animated */);
@@ -387,26 +479,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
if (!mIsAnimating) {
mTaskView.setAlpha(visible ? 1f : 0f);
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.setWindowVisible(visible);
+ }
+ updateSamplingState();
}
}
- /**
- * Updates the handle color based on the task view status bar or background color; if those
- * are transparent it defaults to the background color pulled from system theme attributes.
- */
- private void updateHandleColor(boolean animated) {
- if (mTaskView == null || mTaskView.getTaskInfo() == null) return;
- int color = mBackgroundColor;
- ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription;
- if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) {
- color = taskDescription.getStatusBarColor();
- } else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) {
- color = taskDescription.getBackgroundColor();
- }
- final boolean isRegionDark = Color.luminance(color) <= 0.5;
- mHandleView.updateHandleColor(isRegionDark, animated);
- }
-
/**
* Sets the alpha of both this view and the task view.
*/
@@ -435,6 +514,11 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
*/
public void setAnimating(boolean animating) {
mIsAnimating = animating;
+ if (mIsAnimating) {
+ // Stop sampling while animating -- when animating is done setContentVisibility will
+ // re-trigger sampling if we're visible.
+ updateSamplingState();
+ }
// If we're done animating, apply the correct visibility.
if (!animating) {
setContentVisibility(mIsContentVisible);
@@ -474,6 +558,37 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
}
}
+ private void recreateRegionSamplingHelper() {
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+ if (mMainExecutor == null || mBackgroundExecutor == null
+ || mRegionSamplingProvider == null) {
+ // Null when it's the overflow / don't need sampling then.
+ return;
+ }
+ mRegionSamplingHelper = mRegionSamplingProvider.createHelper(this,
+ new RegionSamplingHelper.SamplingCallback() {
+ @Override
+ public void onRegionDarknessChanged(boolean isRegionDark) {
+ if (mHandleView != null) {
+ mHandleView.updateHandleColor(isRegionDark,
+ true /* animated */);
+ }
+ }
+
+ @Override
+ public Rect getSampledRegion(View sampledView) {
+ return getCaptionSampleRect();
+ }
+
+ @Override
+ public boolean isSamplingEnabled() {
+ return shouldSampleRegion();
+ }
+ }, mMainExecutor, mBackgroundExecutor);
+ }
+
private class HandleViewAccessibilityDelegate extends AccessibilityDelegate {
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull View host,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index eeb5c94c8f816e9f50ccfc2d2922df0e522f786d..07463bb024a28c6e7671fe1a46f0ffd3276b6ba6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -20,8 +20,8 @@ import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.View
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.common.bubbles.DismissView
-import com.android.wm.shell.common.bubbles.RelativeTouchListener
+import com.android.wm.shell.shared.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject
/** Controller for handling drag interactions with [BubbleBarExpandedView] */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
index c91567d7d8be2d5a1aad181d907c449d5492cdaf..e781c07f01a78c5140781bafff67af7ce51ade10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -42,7 +42,9 @@ public class BubbleBarHandleView extends View {
private final @ColorInt int mHandleLightColor;
private final @ColorInt int mHandleDarkColor;
- private @Nullable ObjectAnimator mColorChangeAnim;
+ private @ColorInt int mCurrentColor;
+ @Nullable
+ private ObjectAnimator mColorChangeAnim;
public BubbleBarHandleView(Context context) {
this(context, null /* attrs */);
@@ -88,13 +90,17 @@ public class BubbleBarHandleView extends View {
*
* @param isRegionDark Whether the background behind the handle is dark, and thus the handle
* should be light (and vice versa).
- * @param animated Whether to animate the change, or apply it immediately.
+ * @param animated Whether to animate the change, or apply it immediately.
*/
public void updateHandleColor(boolean isRegionDark, boolean animated) {
int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor;
+ if (newColor == mCurrentColor) {
+ return;
+ }
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
}
+ mCurrentColor = newColor;
if (animated) {
mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor);
mColorChangeAnim.addListener(new AnimatorListenerAdapter() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index ac424532e87b4c0733e9140d09a3e08b25d32598..1c9c195cf718276e18edd4e938aaabf55ffcdf67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -44,9 +44,9 @@ import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.DeviceConfig;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedViewDragController.DragListener;
-import com.android.wm.shell.common.bubbles.BaseBubblePinController;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DismissView;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index 0d72998eb2e8682c33f974c0d8d4f1ef8e2d65ed..514810745e1092bccaf1459553270d97fd711635 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
@@ -219,6 +220,21 @@ class BubbleBarMenuViewController {
}
));
+ if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) {
+ menuActions.add(new BubbleBarMenuView.MenuAction(
+ Icon.createWithResource(resources,
+ R.drawable.desktop_mode_ic_handle_menu_fullscreen),
+ resources.getString(R.string.bubble_fullscreen_text),
+ tintColor,
+ view -> {
+ hideMenu(true /* animated */);
+ if (mListener != null) {
+ mListener.onMoveToFullscreen(bubble);
+ }
+ }
+ ));
+ }
+
return menuActions;
}
@@ -249,5 +265,10 @@ class BubbleBarMenuViewController {
* Dismiss bubble and remove it from the bubble stack
*/
void onDismissBubble(Bubble bubble);
+
+ /**
+ * Move the bubble to fullscreen.
+ */
+ void onMoveToFullscreen(Bubble bubble);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index e108f7be48c73740b3a491e54e4e5cf45197e198..9fd255ded0ad893cf93f2721a5486b9bc12e5a9a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -34,9 +34,9 @@ import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.BubbleViewProvider
import com.android.wm.shell.bubbles.setup
-import com.android.wm.shell.common.bubbles.BubblePopupDrawable
-import com.android.wm.shell.common.bubbles.BubblePopupView
import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
+import com.android.wm.shell.shared.bubbles.BubblePopupView
import kotlin.math.roundToInt
/** Manages bubble education presentation and animation */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
index 651bf022e07d2ae838c13bbb435fad960182dcf2..23ba2bff5ebcdd9bade4d1e6e1a45eaf9c982dce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
@@ -25,8 +25,8 @@ import android.widget.FrameLayout
import androidx.core.view.updateLayoutParams
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/**
* Controller to manage pinning bubble bar to left or right when dragging starts from the bubble bar
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 5b01a0d87b5ed2b446ba65404c1d93b4eae6712b..f03daada4ca0430a959103e75abded34e0632b39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -291,17 +291,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if (hadImeSourceControl != hasImeSourceControl) {
dispatchImeControlTargetChanged(mDisplayId, hasImeSourceControl);
}
+ final boolean hasImeLeash = hasImeSourceControl && imeSourceControl.getLeash() != null;
boolean pendingImeStartAnimation = false;
- boolean canAnimate;
- if (android.view.inputmethod.Flags.refactorInsetsController()) {
- canAnimate = hasImeSourceControl && imeSourceControl.getLeash() != null;
- } else {
- canAnimate = hasImeSourceControl;
- }
-
boolean positionChanged = false;
- if (canAnimate) {
+ if (hasImeLeash) {
if (mAnimation != null) {
final Point lastSurfacePosition = hadImeSourceControl
? mImeSourceControl.getSurfacePosition() : null;
@@ -325,6 +319,13 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// continue the bar to slide to the end (even without visible IME)
mAnimation.cancel();
}
+
+ // Make mImeSourceControl point to the new control before starting the animation.
+ if (hadImeSourceControl && mImeSourceControl != imeSourceControl) {
+ mImeSourceControl.release(SurfaceControl::release);
+ }
+ mImeSourceControl = imeSourceControl;
+
if (positionChanged) {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
// For showing the IME, the leash has to be available first. Hiding
@@ -338,11 +339,6 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
- if (hadImeSourceControl && mImeSourceControl != imeSourceControl) {
- mImeSourceControl.release(SurfaceControl::release);
- }
- mImeSourceControl = imeSourceControl;
-
if (android.view.inputmethod.Flags.refactorInsetsController()) {
if (pendingImeStartAnimation) {
startAnimation(true, true /* forceRestart */);
@@ -465,11 +461,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
private void startAnimation(final boolean show, final boolean forceRestart,
@NonNull final ImeTracker.Token statsToken) {
+ if (mImeSourceControl == null || mImeSourceControl.getLeash() == null) {
+ if (DEBUG) Slog.d(TAG, "No leash available, not starting the animation.");
+ return;
+ }
if (android.view.inputmethod.Flags.refactorInsetsController()) {
- if (mImeSourceControl == null || mImeSourceControl.getLeash() == null) {
- if (DEBUG) Slog.d(TAG, "No leash available, not starting the animation.");
- return;
- } else if (!mImeRequestedVisible && show) {
+ if (!mImeRequestedVisible && show) {
// we have a control with leash, but the IME was not requested visible before,
// therefore aborting the show animation.
Slog.e(TAG, "IME was not requested visible, not starting the show animation.");
@@ -478,7 +475,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
- if (imeSource == null || mImeSourceControl == null) {
+ if (imeSource == null) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
return;
}
@@ -515,8 +512,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
mAnimation.cancel();
}
- final float defaultY = mImeSourceControl.getSurfacePosition().y;
- final float x = mImeSourceControl.getSurfacePosition().x;
+ final InsetsSourceControl animatingControl = new InsetsSourceControl(mImeSourceControl);
+ final SurfaceControl animatingLeash = animatingControl.getLeash();
+ final float defaultY = animatingControl.getSurfacePosition().y;
+ final float x = animatingControl.getSurfacePosition().x;
final float hiddenY = defaultY + mImeFrame.height();
final float shownY = defaultY;
final float startY = show ? hiddenY : shownY;
@@ -538,13 +537,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mAnimation.addUpdateListener(animation -> {
SurfaceControl.Transaction t = mTransactionPool.acquire();
float value = (float) animation.getAnimatedValue();
- if (!android.view.inputmethod.Flags.refactorInsetsController() || (
- mImeSourceControl != null && mImeSourceControl.getLeash() != null)) {
- t.setPosition(mImeSourceControl.getLeash(), x, value);
- final float alpha = (mAnimateAlpha || isFloating)
- ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
- t.setAlpha(mImeSourceControl.getLeash(), alpha);
- }
+ t.setPosition(animatingLeash, x, value);
+ final float alpha = (mAnimateAlpha || isFloating)
+ ? (value - hiddenY) / (shownY - hiddenY) : 1f;
+ t.setAlpha(animatingLeash, alpha);
dispatchPositionChanged(mDisplayId, imeTop(value), t);
t.apply();
mTransactionPool.release(t);
@@ -561,7 +557,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
ValueAnimator valueAnimator = (ValueAnimator) animation;
float value = (float) valueAnimator.getAnimatedValue();
SurfaceControl.Transaction t = mTransactionPool.acquire();
- t.setPosition(mImeSourceControl.getLeash(), x, value);
+ t.setPosition(animatingLeash, x, value);
if (DEBUG) {
Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+ imeTop(hiddenY) + "->" + imeTop(shownY)
@@ -573,19 +569,19 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
final float alpha = (mAnimateAlpha || isFloating)
? (value - hiddenY) / (shownY - hiddenY)
: 1.f;
- t.setAlpha(mImeSourceControl.getLeash(), alpha);
+ t.setAlpha(animatingLeash, alpha);
if (mAnimationDirection == DIRECTION_SHOW) {
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
- t.show(mImeSourceControl.getLeash());
+ t.show(animatingLeash);
}
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId, mAnimationDirection, alpha, value, endY,
- Objects.toString(mImeSourceControl.getLeash()),
- Objects.toString(mImeSourceControl.getInsetsHint()),
- Objects.toString(mImeSourceControl.getSurfacePosition()),
+ Objects.toString(animatingLeash),
+ Objects.toString(animatingControl.getInsetsHint()),
+ Objects.toString(animatingControl.getSurfacePosition()),
Objects.toString(mImeFrame));
}
t.apply();
@@ -599,31 +595,23 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId,
- Objects.toString(mImeSourceControl.getInsetsHint()));
+ Objects.toString(animatingControl.getInsetsHint()));
}
}
@Override
public void onAnimationEnd(Animator animation) {
- boolean hasLeash =
- mImeSourceControl != null && mImeSourceControl.getLeash() != null;
if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
SurfaceControl.Transaction t = mTransactionPool.acquire();
if (!mCancelled) {
- if (!android.view.inputmethod.Flags.refactorInsetsController()
- || hasLeash) {
- t.setPosition(mImeSourceControl.getLeash(), x, endY);
- t.setAlpha(mImeSourceControl.getLeash(), 1.f);
- }
+ t.setPosition(animatingLeash, x, endY);
+ t.setAlpha(animatingLeash, 1.f);
}
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
- if (!android.view.inputmethod.Flags.refactorInsetsController()
- || hasLeash) {
- t.hide(mImeSourceControl.getLeash());
- }
+ t.hide(animatingLeash);
removeImeSurface(mDisplayId);
ImeTracker.forLogging().onHidden(mStatsToken);
} else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) {
@@ -636,13 +624,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId, mAnimationDirection, endY,
- Objects.toString(
- mImeSourceControl != null ? mImeSourceControl.getLeash()
- : "null"),
- Objects.toString(mImeSourceControl != null
- ? mImeSourceControl.getInsetsHint() : "null"),
- Objects.toString(mImeSourceControl != null
- ? mImeSourceControl.getSurfacePosition() : "null"),
+ Objects.toString(animatingLeash),
+ Objects.toString(animatingControl.getInsetsHint()),
+ Objects.toString(animatingControl.getSurfacePosition()),
Objects.toString(mImeFrame));
}
t.apply();
@@ -650,6 +634,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mAnimationDirection = DIRECTION_NONE;
mAnimation = null;
+ animatingControl.release(SurfaceControl::release);
}
});
if (!show) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index fad3dee1f927109cefa8bebf1fb89c773604e11c..1929729eb1ad2d7af1a2ba789140a38219ea2b71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -42,6 +42,7 @@ public class ScreenshotUtils {
.setSourceCrop(crop)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
+ .setHintForSeamlessTransition(true)
.build()));
}
@@ -78,6 +79,9 @@ public class ScreenshotUtils {
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
mTransaction.reparent(mScreenshot, mParentSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
+ if (buffer.containsHdrLayers()) {
+ mTransaction.setDimmingEnabled(mScreenshot, false);
+ }
mTransaction.show(mScreenshot);
mTransaction.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index dcf84d927ad3f1fae2bf80606c4552b178f8d5c9..7070ce99b24c1e1f0661473d3dbdccdd713bc256 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -24,7 +24,6 @@ import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Rect
import android.os.RemoteException
-import android.os.SystemProperties
import android.util.DisplayMetrics
import android.util.Log
import android.util.Pair
@@ -178,9 +177,7 @@ object PipUtils {
"org.chromium.arc", 0)
val isTv = AppGlobals.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK, 0)
- isPip2ExperimentEnabled = SystemProperties.getBoolean(
- "persist.wm_shell.pip2", false) ||
- (Flags.enablePip2Implementation() && !isArc && !isTv)
+ isPip2ExperimentEnabled = Flags.enablePip2() && !isArc && !isTv
}
return isPip2ExperimentEnabled as Boolean
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 972b78f6ca9af692d0627058c6e8a17854302b77..6146ecd9ade6c516099b63a0ed124aec2141508c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -831,7 +831,6 @@ public class CompatUIController implements OnDisplaysChangedListener,
*/
static class CompatUIHintsState {
boolean mHasShownSizeCompatHint;
- boolean mHasShownCameraCompatHint;
boolean mHasShownUserAspectRatioSettingsButtonHint;
}
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 42937c134e7fc85f96ce6a362b3ae8bfb861ceb7..4adea233b734eb1e99d7b78392789b7d21d659b1 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
@@ -148,7 +148,11 @@ import java.util.function.IntPredicate;
* dependencies that are device/form factor SystemUI implementation specific should go into their
* respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.)
*/
-@Module(includes = WMShellConcurrencyModule.class)
+@Module(
+ includes = {
+ WMShellConcurrencyModule.class,
+ WMShellCoroutinesModule.class
+ })
public abstract class WMShellBaseModule {
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt
index a489c4ffdd949ad1ce2d03e1198b55c2a48622e4..cc47dbb78af20ecc0c1e236d526147e542014765 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.dagger
+import android.os.Handler
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
@@ -24,41 +25,56 @@ import dagger.Provides
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
-/** Providers for various WmShell-specific coroutines-related constructs. */
+/**
+ * Providers for various WmShell-specific coroutines-related constructs.
+ *
+ * Providers of [MainCoroutineDispatcher] intentionally creates the dispatcher with a [Handler]
+ * backing it instead of a [ShellExecutor] because [ShellExecutor.asCoroutineDispatcher] will
+ * create a [CoroutineDispatcher] whose [CoroutineDispatcher.isDispatchNeeded] is effectively never
+ * dispatching. This is because even if dispatched, the backing [ShellExecutor.execute] always runs
+ * the [Runnable] immediately if called from the same thread, whereas
+ * [Handler.asCoroutineDispatcher] will create a [MainCoroutineDispatcher] that correctly
+ * dispatches (queues) when [CoroutineDispatcher.isDispatchNeeded] is true using [Handler.post].
+ * For callers that do need a non-dispatching version, [MainCoroutineDispatcher.immediate] is
+ * available.
+ */
@Module
class WMShellCoroutinesModule {
@Provides
@ShellMainThread
- fun provideMainDispatcher(@ShellMainThread mainExecutor: ShellExecutor): CoroutineDispatcher =
- mainExecutor.asCoroutineDispatcher()
+ fun provideMainDispatcher(
+ @ShellMainThread mainHandler: Handler
+ ): MainCoroutineDispatcher = mainHandler.asCoroutineDispatcher()
@Provides
@ShellBackgroundThread
fun provideBackgroundDispatcher(
- @ShellBackgroundThread backgroundExecutor: ShellExecutor
- ): CoroutineDispatcher = backgroundExecutor.asCoroutineDispatcher()
+ @ShellBackgroundThread backgroundHandler: Handler
+ ): MainCoroutineDispatcher = backgroundHandler.asCoroutineDispatcher()
@Provides
@WMSingleton
@ShellMainThread
fun provideApplicationScope(
- @ShellMainThread applicationDispatcher: CoroutineDispatcher,
+ @ShellMainThread applicationDispatcher: MainCoroutineDispatcher,
): CoroutineScope = CoroutineScope(applicationDispatcher)
@Provides
@WMSingleton
@ShellBackgroundThread
fun provideBackgroundCoroutineScope(
- @ShellBackgroundThread backgroundDispatcher: CoroutineDispatcher,
+ @ShellBackgroundThread backgroundDispatcher: MainCoroutineDispatcher,
): CoroutineScope = CoroutineScope(backgroundDispatcher)
@Provides
@WMSingleton
@ShellBackgroundThread
fun provideBackgroundCoroutineContext(
- @ShellBackgroundThread backgroundDispatcher: CoroutineDispatcher
+ @ShellBackgroundThread backgroundDispatcher: MainCoroutineDispatcher
): CoroutineContext = backgroundDispatcher + SupervisorJob()
}
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 4db8a82eb5afe80cd5ba67447bc80d8544136f7e..7054c17cfeb0645a05e88e4407c608aa6ce12cd2 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
@@ -38,6 +38,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -59,6 +60,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
+import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
@@ -72,6 +74,7 @@ import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator;
import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
import com.android.wm.shell.desktopmode.education.AppHandleEducationFilter;
import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
import com.android.wm.shell.draganddrop.DragAndDropController;
@@ -117,6 +120,8 @@ import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import kotlinx.coroutines.CoroutineScope;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -236,8 +241,10 @@ public abstract class WMShellModule {
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
- Optional desktopTasksLimiter) {
+ Optional desktopTasksLimiter,
+ Optional desktopActivityOrientationHandler) {
if (DesktopModeStatus.canEnterDesktopMode(context)) {
return new DesktopModeWindowDecorViewModel(
context,
@@ -258,8 +265,10 @@ public abstract class WMShellModule {
rootTaskDisplayAreaOrganizer,
interactionJankMonitor,
genericLinksParser,
+ assistContentRequester,
multiInstanceHelper,
- desktopTasksLimiter);
+ desktopTasksLimiter,
+ desktopActivityOrientationHandler);
}
return new CaptionWindowDecorViewModel(
context,
@@ -285,6 +294,15 @@ public abstract class WMShellModule {
return new AppToWebGenericLinksParser(context, mainExecutor);
}
+ @Provides
+ static AssistContentRequester provideAssistContentRequester(
+ Context context,
+ @ShellMainThread ShellExecutor shellExecutor,
+ @ShellBackgroundThread ShellExecutor bgExecutor
+ ) {
+ return new AssistContentRequester(context, shellExecutor, bgExecutor);
+ }
+
//
// Freeform
//
@@ -675,6 +693,24 @@ public abstract class WMShellModule {
return new DesktopModeTaskRepository();
}
+ @WMSingleton
+ @Provides
+ static Optional provideActivityOrientationHandler(
+ Context context,
+ ShellInit shellInit,
+ ShellTaskOrganizer shellTaskOrganizer,
+ TaskStackListenerImpl taskStackListener,
+ ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
+ @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository
+ ) {
+ if (DesktopModeStatus.canEnterDesktopMode(context)) {
+ return Optional.of(new DesktopActivityOrientationChangeHandler(
+ context, shellInit, shellTaskOrganizer, taskStackListener,
+ toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository));
+ }
+ return Optional.empty();
+ }
+
@WMSingleton
@Provides
static Optional provideDesktopTasksTransitionObserver(
@@ -722,6 +758,17 @@ public abstract class WMShellModule {
return new AppHandleEducationFilter(context, appHandleEducationDatastoreRepository);
}
+ @WMSingleton
+ @Provides
+ static AppHandleEducationController provideAppHandleEducationController(
+ AppHandleEducationFilter appHandleEducationFilter,
+ ShellTaskOrganizer shellTaskOrganizer,
+ AppHandleEducationDatastoreRepository appHandleEducationDatastoreRepository,
+ @ShellMainThread CoroutineScope applicationScope) {
+ return new AppHandleEducationController(appHandleEducationFilter,
+ shellTaskOrganizer, appHandleEducationDatastoreRepository, applicationScope);
+ }
+
//
// Drag and drop
//
@@ -763,7 +810,8 @@ public abstract class WMShellModule {
@Provides
static Object provideIndependentShellComponentsToCreate(
DragAndDropController dragAndDropController,
- Optional desktopTasksTransitionObserverOptional
+ Optional desktopTasksTransitionObserverOptional,
+ AppHandleEducationController appHandleEducationController
) {
return new Object();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..59e006879da8222168ecbe3d5a32f45da7b5f30c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.pm.ActivityInfo.ScreenOrientation
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.graphics.Rect
+import android.util.Size
+import android.window.WindowContainerTransaction
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.TaskStackListenerCallback
+import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+
+/** Handles task resizing to respect orientation change of non-resizeable activities in desktop. */
+class DesktopActivityOrientationChangeHandler(
+ context: Context,
+ shellInit: ShellInit,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val taskStackListener: TaskStackListenerImpl,
+ private val resizeHandler: ToggleResizeDesktopTaskTransitionHandler,
+ private val taskRepository: DesktopModeTaskRepository,
+) {
+
+ init {
+ if (DesktopModeStatus.canEnterDesktopMode(context)) {
+ shellInit.addInitCallback({ onInit() }, this)
+ }
+ }
+
+ private fun onInit() {
+ taskStackListener.addListener(object : TaskStackListenerCallback {
+ override fun onActivityRequestedOrientationChanged(
+ taskId: Int,
+ @ScreenOrientation requestedOrientation: Int
+ ) {
+ // Handle requested screen orientation changes at runtime.
+ handleActivityOrientationChange(taskId, requestedOrientation)
+ }
+ })
+ }
+
+ /**
+ * Triggered with onTaskInfoChanged to handle:
+ * * New activity launching from same task with different orientation
+ * * Top activity closing in same task with different orientation to previous activity
+ */
+ fun handleActivityOrientationChange(oldTask: RunningTaskInfo, newTask: RunningTaskInfo) {
+ val newTopActivityInfo = newTask.topActivityInfo ?: return
+ val oldTopActivityInfo = oldTask.topActivityInfo ?: return
+ // Check if screen orientation is different from old task info so there is no duplicated
+ // calls to handle runtime requested orientation changes.
+ if (oldTopActivityInfo.screenOrientation != newTopActivityInfo.screenOrientation) {
+ handleActivityOrientationChange(newTask.taskId, newTopActivityInfo.screenOrientation)
+ }
+ }
+
+ private fun handleActivityOrientationChange(
+ taskId: Int,
+ @ScreenOrientation requestedOrientation: Int
+ ) {
+ if (!Flags.respectOrientationChangeForUnresizeable()) return
+ val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: return
+ if (!isDesktopModeShowing(task.displayId) || !task.isFreeform || task.isResizeable) return
+
+ val taskBounds = task.configuration.windowConfiguration.bounds
+ val taskHeight = taskBounds.height()
+ val taskWidth = taskBounds.width()
+ if (taskWidth == taskHeight) return
+ val orientation =
+ if (taskWidth > taskHeight) ORIENTATION_LANDSCAPE else ORIENTATION_PORTRAIT
+
+ // Non-resizeable activity requested opposite orientation.
+ if (orientation == ORIENTATION_PORTRAIT
+ && ActivityInfo.isFixedOrientationLandscape(requestedOrientation)
+ || orientation == ORIENTATION_LANDSCAPE
+ && ActivityInfo.isFixedOrientationPortrait(requestedOrientation)) {
+
+ val finalSize = Size(taskHeight, taskWidth)
+ // Use the center x as the resizing anchor point.
+ val left = taskBounds.centerX() - finalSize.width / 2
+ val right = left + finalSize.width
+ val finalBounds = Rect(left, taskBounds.top, right, taskBounds.top + finalSize.height)
+
+ val wct = WindowContainerTransaction().setBounds(task.token, finalBounds)
+ resizeHandler.startTransition(wct)
+ }
+ }
+
+ private fun isDesktopModeShowing(displayId: Int): Boolean =
+ taskRepository.getVisibleTaskCount(displayId) > 0
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 336e5e3af2c6d311f7d6689e813d241292a6b5cc..063747494a827820a2286e08ae469dd7b98651c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -22,6 +22,7 @@ import android.app.TaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
import android.os.IBinder
+import android.os.SystemProperties
import android.os.Trace
import android.util.SparseArray
import android.view.SurfaceControl
@@ -52,8 +53,6 @@ import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
-const val VISIBLE_TASKS_COUNTER_NAME = "DESKTOP_MODE_VISIBLE_TASKS"
-
/**
* A [Transitions.TransitionObserver] that observes transitions and the proposed changes to log
* appropriate desktop mode session log events. This observes transitions related to desktop mode
@@ -307,6 +306,8 @@ class DesktopModeLoggerTransitionObserver(
VISIBLE_TASKS_COUNTER_NAME,
postTransitionVisibleFreeformTasks.size().toLong()
)
+ SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
+ postTransitionVisibleFreeformTasks.size().toString())
}
// old tasks that were resized or repositioned
// TODO(b/347935387): Log changes only once they are stable.
@@ -326,6 +327,8 @@ class DesktopModeLoggerTransitionObserver(
VISIBLE_TASKS_COUNTER_NAME,
postTransitionVisibleFreeformTasks.size().toLong()
)
+ SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
+ postTransitionVisibleFreeformTasks.size().toString())
}
}
}
@@ -431,4 +434,12 @@ class DesktopModeLoggerTransitionObserver(
return this.type == WindowManager.TRANSIT_TO_FRONT &&
this.flags == WindowManager.TRANSIT_FLAG_IS_RECENTS
}
+
+ companion object {
+ @VisibleForTesting
+ const val VISIBLE_TASKS_COUNTER_NAME = "desktop_mode_visible_tasks"
+ @VisibleForTesting
+ const val VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY =
+ "debug.tracing." + VISIBLE_TASKS_COUNTER_NAME
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index b68b436f2c1b3f7d847b6cbcd937b70fb1d615e7..c8ffe28da79c28142903e1b0fb99b5c060d384bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -171,6 +171,18 @@ fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
minOf(appBounds.height(), appBounds.width()).toFloat()
}
+/** Returns true if task's width or height is maximized else returns false. */
+fun isTaskWidthOrHeightEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
+ return taskBounds.width() == stableBounds.width() ||
+ taskBounds.height() == stableBounds.height()
+}
+
+/** Returns true if task bound is equal to stable bounds else returns false. */
+fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
+ return taskBounds.width() == stableBounds.width() &&
+ taskBounds.height() == stableBounds.height()
+}
+
/**
* Calculates the desired initial bounds for applications in desktop windowing. This is done as a
* scale of the screen bounds.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 2852631656b52581eec02fc035e988d6144c4bff..1d16980c617d0d298fe5b065265b1421062f4c5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -169,6 +169,9 @@ class DesktopTasksController(
}
}
+ @VisibleForTesting
+ var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null
+
/** Task id of the task currently being dragged from fullscreen/split. */
val draggingTaskId
get() = dragToDesktopTransitionHandler.draggingTaskId
@@ -610,13 +613,10 @@ class DesktopTasksController(
val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
val destinationBounds = Rect()
- val isMaximized = if (taskInfo.isResizeable) {
- currentTaskBounds == stableBounds
- } else {
- currentTaskBounds.width() == stableBounds.width()
- || currentTaskBounds.height() == stableBounds.height()
- }
-
+ val isMaximized = isTaskMaximized(taskInfo, stableBounds)
+ // If the task is currently maximized, we will toggle it not to be and vice versa. This is
+ // helpful to eliminate the current task from logic to calculate taskbar corner rounding.
+ val willMaximize = !isMaximized
if (isMaximized) {
// The desktop task is at the maximized width and/or height of the stable bounds.
// If the task's pre-maximize stable bounds were saved, toggle the task to those bounds.
@@ -651,6 +651,18 @@ class DesktopTasksController(
}
}
+
+
+ val shouldRestoreToSnap =
+ isMaximized && isTaskSnappedToHalfScreen(taskInfo, destinationBounds)
+
+ logD("willMaximize = %s", willMaximize)
+ logD("shouldRestoreToSnap = %s", shouldRestoreToSnap)
+
+ val doesAnyTaskRequireTaskbarRounding = willMaximize || shouldRestoreToSnap ||
+ doesAnyTaskRequireTaskbarRounding(taskInfo.displayId, taskInfo.taskId)
+
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding)
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
@@ -659,23 +671,98 @@ class DesktopTasksController(
}
}
+ private fun isTaskMaximized(
+ taskInfo: RunningTaskInfo,
+ stableBounds: Rect
+ ): Boolean {
+ val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
+
+ return if (taskInfo.isResizeable) {
+ isTaskBoundsEqual(currentTaskBounds, stableBounds)
+ } else {
+ isTaskWidthOrHeightEqual(currentTaskBounds, stableBounds)
+ }
+ }
+
+ private fun isMaximizedToStableBoundsEdges(
+ taskInfo: RunningTaskInfo,
+ stableBounds: Rect
+ ): Boolean {
+ val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
+ return isTaskBoundsEqual(currentTaskBounds, stableBounds)
+ }
+
+ /** Returns if current task bound is snapped to half screen */
+ private fun isTaskSnappedToHalfScreen(
+ taskInfo: RunningTaskInfo,
+ taskBounds: Rect = taskInfo.configuration.windowConfiguration.bounds
+ ): Boolean =
+ getSnapBounds(taskInfo, SnapPosition.LEFT) == taskBounds ||
+ getSnapBounds(taskInfo, SnapPosition.RIGHT) == taskBounds
+
+ @VisibleForTesting
+ fun doesAnyTaskRequireTaskbarRounding(
+ displayId: Int,
+ excludeTaskId: Int? = null,
+ ): Boolean {
+ val doesAnyTaskRequireTaskbarRounding =
+ taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
+ // exclude current task since maximize/restore transition has not taken place yet.
+ .filterNot { taskId -> taskId == excludeTaskId }
+ .any { taskId ->
+ val taskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)!!
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
+ val stableBounds = Rect().apply { displayLayout?.getStableBounds(this) }
+ logD("taskInfo = %s", taskInfo)
+ logD(
+ "isTaskSnappedToHalfScreen(taskInfo) = %s",
+ isTaskSnappedToHalfScreen(taskInfo)
+ )
+ logD(
+ "isMaximizedToStableBoundsEdges(taskInfo, stableBounds) = %s",
+ isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
+ )
+ isTaskSnappedToHalfScreen(taskInfo)
+ || isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
+ }
+
+ logD("doesAnyTaskRequireTaskbarRounding = %s", doesAnyTaskRequireTaskbarRounding)
+ return doesAnyTaskRequireTaskbarRounding
+ }
+
/**
* Quick-resize to the right or left half of the stable bounds.
*
* @param taskInfo current task that is being snap-resized via dragging or maximize menu button
+ * @param taskSurface the leash of the task being dragged
* @param currentDragBounds current position of the task leash being dragged (or current task
* bounds if being snapped resize via maximize menu button)
* @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
*/
fun snapToHalfScreen(
taskInfo: RunningTaskInfo,
+ taskSurface: SurfaceControl,
currentDragBounds: Rect,
position: SnapPosition
) {
val destinationBounds = getSnapBounds(taskInfo, position)
+ if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) {
+ // Handle the case where we attempt to snap resize when already snap resized: the task
+ // position won't need to change but we want to animate the surface going back to the
+ // snapped position from the "dragged-to-the-edge" position.
+ if (destinationBounds != currentDragBounds) {
+ returnToDragStartAnimator.start(
+ taskInfo.taskId,
+ taskSurface,
+ startBounds = currentDragBounds,
+ endBounds = destinationBounds,
+ isResizable = taskInfo.isResizeable
+ )
+ }
+ return
+ }
- if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
-
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds)
@@ -703,13 +790,14 @@ class DesktopTasksController(
taskInfo.taskId,
taskSurface,
startBounds = currentDragBounds,
- endBounds = dragStartBounds
+ endBounds = dragStartBounds,
+ isResizable = taskInfo.isResizeable,
)
} else {
interactionJankMonitor.begin(
taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"
)
- snapToHalfScreen(taskInfo, currentDragBounds, position)
+ snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position)
}
}
@@ -806,6 +894,10 @@ class DesktopTasksController(
.mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
.reversed() // Start from the back so the front task is brought forward last
.forEach { task -> wct.reorder(task.token, /* onTop= */ true) }
+
+ taskbarDesktopTaskListener?.
+ onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))
+
return taskToMinimize
}
@@ -821,6 +913,7 @@ class DesktopTasksController(
val intent = Intent(context, DesktopWallpaperActivity::class.java)
val options =
ActivityOptions.makeBasic().apply {
+ launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
}
@@ -1156,6 +1249,12 @@ class DesktopTasksController(
) {
wct.removeTask(task.token)
}
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+ doesAnyTaskRequireTaskbarRounding(
+ task.displayId,
+ task.id
+ )
+ )
return if (wct.isEmpty) null else wct
}
@@ -1450,6 +1549,8 @@ class DesktopTasksController(
}
// A freeform drag-move ended, remove the indicator immediately.
releaseVisualIndicator()
+ taskbarDesktopTaskListener
+ ?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(taskInfo.displayId))
}
/**
@@ -1686,17 +1787,39 @@ class DesktopTasksController(
}
}
+ private val mTaskbarDesktopTaskListener: TaskbarDesktopTaskListener =
+ object : TaskbarDesktopTaskListener {
+ override fun onTaskbarCornerRoundingUpdate(
+ hasTasksRequiringTaskbarRounding: Boolean) {
+ ProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: onTaskbarCornerRoundingUpdate " +
+ "doesAnyTaskRequireTaskbarRounding=%s",
+ hasTasksRequiringTaskbarRounding
+ )
+
+ remoteListener.call { l ->
+ l.onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding)
+ }
+ }
+ }
+
init {
remoteListener =
SingleInstanceRemoteListener(
controller,
{ c ->
- c.taskRepository.addVisibleTasksListener(
- listener,
- c.mainExecutor
- )
+ run {
+ c.taskRepository.addVisibleTasksListener(listener, c.mainExecutor)
+ c.taskbarDesktopTaskListener = mTaskbarDesktopTaskListener
+ }
},
- { c -> c.taskRepository.removeVisibleTasksListener(listener) }
+ { c ->
+ run {
+ c.taskRepository.removeVisibleTasksListener(listener)
+ c.taskbarDesktopTaskListener = null
+ }
+ }
)
}
@@ -1779,6 +1902,16 @@ class DesktopTasksController(
private const val TAG = "DesktopTasksController"
}
+ /** Defines interface for classes that can listen to changes for task resize. */
+ // TODO(b/343931111): Migrate to using TransitionObservers when ready
+ interface TaskbarDesktopTaskListener {
+ /**
+ * [hasTasksRequiringTaskbarRounding] is true when a task is either maximized or snapped
+ * left/right and rounded corners are enabled.
+ */
+ fun onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding: Boolean)
+ }
+
/** The positions on a screen that a task can snap to. */
enum class SnapPosition {
RIGHT,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index 8ebdfdcf4731e779c047ab08dff2b3d0e2bad4b7..c2acb87222d1cd061462e73621ccdb08d02d6719 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -27,4 +27,10 @@ interface IDesktopTaskListener {
/** @deprecated this is no longer supported. */
oneway void onStashedChanged(int displayId, boolean stashed);
+
+ /**
+ * Shows taskbar corner radius when running desktop tasks are updated if
+ * [hasTasksRequiringTaskbarRounding] is true.
+ */
+ oneway void onTaskbarCornerRoundingUpdate(boolean hasTasksRequiringTaskbarRounding);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
index 4c5258f2bfcde9d61211062ca7c17103000229cd..f4df42cde10f04c8bd374ae518e410c5e057ab86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
@@ -48,7 +48,13 @@ class ReturnToDragStartAnimator(
}
/** Builds new animator and starts animation of task leash reposition. */
- fun start(taskId: Int, taskSurface: SurfaceControl, startBounds: Rect, endBounds: Rect) {
+ fun start(
+ taskId: Int,
+ taskSurface: SurfaceControl,
+ startBounds: Rect,
+ endBounds: Rect,
+ isResizable: Boolean
+ ) {
val tx = transactionSupplier.get()
boundsAnimator?.cancel()
@@ -81,11 +87,13 @@ class ReturnToDragStartAnimator(
.apply()
taskRepositionAnimationListener.onAnimationEnd(taskId)
boundsAnimator = null
- Toast.makeText(
- context,
- R.string.desktop_mode_non_resizable_snap_text,
- Toast.LENGTH_SHORT
- ).show()
+ if (!isResizable) {
+ Toast.makeText(
+ context,
+ R.string.desktop_mode_non_resizable_snap_text,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
}
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6013e97977d858b6f6ead8a123fb3a8a5fc16c83
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode.education
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.SystemProperties
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/**
+ * Controls app handle education end to end.
+ *
+ * Listen to the user trigger for app handle education, calls an api to check if the education
+ * should be shown and calls an api to show education.
+ */
+@OptIn(kotlinx.coroutines.FlowPreview::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class AppHandleEducationController(
+ private val appHandleEducationFilter: AppHandleEducationFilter,
+ shellTaskOrganizer: ShellTaskOrganizer,
+ private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository,
+ @ShellMainThread private val applicationCoroutineScope: CoroutineScope
+) {
+ init {
+ runIfEducationFeatureEnabled {
+ // TODO: b/361038716 - Use app handle state flow instead of focus task change flow
+ val focusTaskChangeFlow = focusTaskChangeFlow(shellTaskOrganizer)
+ applicationCoroutineScope.launch {
+ // Central block handling the app's educational flow end-to-end.
+ // This flow listens to the changes to the result of
+ // [WindowingEducationProto#hasEducationViewedTimestampMillis()] in datastore proto object
+ isEducationViewedFlow()
+ .flatMapLatest { isEducationViewed ->
+ if (isEducationViewed) {
+ // If the education is viewed then return emptyFlow() that completes immediately.
+ // This will help us to not listen to focus task changes after the education has
+ // been viewed already.
+ emptyFlow()
+ } else {
+ // This flow listens for focus task changes, which trigger the app handle education.
+ focusTaskChangeFlow
+ .filter { runningTaskInfo ->
+ runningTaskInfo.topActivityInfo?.packageName?.let {
+ appHandleEducationFilter.shouldShowAppHandleEducation(it)
+ } ?: false && runningTaskInfo.windowingMode != WINDOWING_MODE_FREEFORM
+ }
+ .distinctUntilChanged()
+ }
+ }
+ .debounce(
+ APP_HANDLE_EDUCATION_DELAY) // Wait for few seconds, if the focus task changes.
+ // During the delay then current emission will be cancelled.
+ .flowOn(Dispatchers.IO)
+ .collectLatest {
+ // Fire and forget show education suspend function, manage entire lifecycle of
+ // tooltip in UI class.
+ }
+ }
+ }
+ }
+
+ private inline fun runIfEducationFeatureEnabled(block: () -> Unit) {
+ if (Flags.enableDesktopWindowingAppHandleEducation()) block()
+ }
+
+ private fun isEducationViewedFlow(): Flow =
+ appHandleEducationDatastoreRepository.dataStoreFlow
+ .map { preferences -> preferences.hasEducationViewedTimestampMillis() }
+ .distinctUntilChanged()
+
+ private fun focusTaskChangeFlow(shellTaskOrganizer: ShellTaskOrganizer): Flow =
+ callbackFlow {
+ val focusTaskChange = ShellTaskOrganizer.FocusListener { taskInfo -> trySend(taskInfo) }
+ shellTaskOrganizer.addFocusListener(focusTaskChange)
+ awaitClose { shellTaskOrganizer.removeFocusListener(focusTaskChange) }
+ }
+
+ private companion object {
+ val APP_HANDLE_EDUCATION_DELAY: Long
+ get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
index a7fff8af99faf38591a95328fd7f41dff28fe641..f420c5be456ff46532ee5b239ec7bee917c1168a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
@@ -25,9 +25,12 @@ import androidx.datastore.core.Serializer
import androidx.datastore.dataStoreFile
import com.android.framework.protobuf.InvalidProtocolBufferException
import com.android.internal.annotations.VisibleForTesting
+import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.time.Duration
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
/**
@@ -46,17 +49,26 @@ constructor(private val dataStore: DataStore) {
serializer = WindowingEducationProtoSerializer,
produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) }))
+ /** Provides dataStore.data flow and handles exceptions thrown during collection */
+ val dataStoreFlow: Flow =
+ dataStore.data.catch { exception ->
+ // dataStore.data throws an IOException when an error is encountered when reading data
+ if (exception is IOException) {
+ Log.e(
+ TAG,
+ "Error in reading app handle education related data from datastore, data is " +
+ "stored in a file named $APP_HANDLE_EDUCATION_DATASTORE_FILEPATH",
+ exception)
+ } else {
+ throw exception
+ }
+ }
+
/**
* Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the
* DataStore is empty or there's an error reading, it returns the default value of Proto.
*/
- suspend fun windowingEducationProto(): WindowingEducationProto =
- try {
- dataStore.data.first()
- } catch (e: Exception) {
- Log.e(TAG, "Unable to read from datastore")
- WindowingEducationProto.getDefaultInstance()
- }
+ suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first()
/**
* Updates [AppHandleEducation.appUsageStats] and
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
index 0acc7df98d1cd0d5f5fed2fc85d79d63c54f4169..faa97ac4512fb9e5f78c6541e40cda8fc6e97575 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
@@ -98,9 +98,8 @@ Don't:
### Exposing shared code for use in Launcher
Launcher doesn't currently build against the Shell library, but needs to have access to some shared
AIDL interfaces and constants. Currently, all AIDL files, and classes under the
-`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that
+`com.android.wm.shell.shared` package are automatically built into the `SystemUISharedLib` that
Launcher uses.
-If the new code doesn't fall into those categories, they can be added explicitly in the Shell's
-[Android.bp](/libs/WindowManager/Shell/Android.bp) file under the
-`wm_shell_util-sources` filegroup.
\ No newline at end of file
+If the new code doesn't fall into those categories, they should be moved to the Shell shared
+package (`com.android.wm.shell.shared`) under the `WindowManager-Shell-shared` library.
\ No newline at end of file
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 e4cd10f37d37fa5ad3b5490a9795305c807fe0b1..ab222c9cdbf6cfc0542fc67d0d3eeccbccb36369 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
@@ -99,6 +99,7 @@ import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.Optional;
+import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -831,6 +832,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPictureInPictureParams.getTitle());
mPipParamsChangedForwarder.notifySubtitleChanged(
mPictureInPictureParams.getSubtitle());
+ logRemoteActions(mPictureInPictureParams);
}
mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
@@ -1112,6 +1114,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
applyNewPictureInPictureParams(newParams);
mPictureInPictureParams = newParams;
+ logRemoteActions(mPictureInPictureParams);
}
@Override
@@ -1420,6 +1423,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
}
+ private void logRemoteActions(@NonNull PictureInPictureParams params) {
+ StringJoiner sj = new StringJoiner("|", "[", "]");
+ if (params.hasSetActions()) {
+ params.getActions().forEach((action) -> sj.add(action.getTitle()));
+ }
+
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: PIP remote actions=%s", TAG, sj.toString());
+ }
+
/**
* Animates resizing of the pinned stack given the duration.
*/
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 0d2b8e70422dd36b20cf3eb029fcd783d3b06e28..06d231144d81eded7cfdb53eb7e91f63d136c68a 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
@@ -35,9 +35,9 @@ import androidx.annotation.NonNull;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
index 8a9302bcfc98f78aeb40825ce51a4ffa21d0f908..8ebdc96c21a32ccd5530c2f553d0884fba9cfe4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Rect;
+import android.view.Surface;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
@@ -51,8 +52,10 @@ public class PipEnterExitAnimator extends ValueAnimator
@NonNull private final SurfaceControl mLeash;
private final SurfaceControl.Transaction mStartTransaction;
- private final int mEnterAnimationDuration;
+ private final SurfaceControl.Transaction mFinishTransaction;
+ private final int mEnterExitAnimationDuration;
private final @BOUNDS int mDirection;
+ private final @Surface.Rotation int mRotation;
// optional callbacks for tracking animation start and end
@Nullable private Runnable mAnimationStartCallback;
@@ -62,37 +65,59 @@ public class PipEnterExitAnimator extends ValueAnimator
private final Rect mStartBounds = new Rect();
private final Rect mEndBounds = new Rect();
+ @Nullable private final Rect mSourceRectHint;
+ private final Rect mSourceRectHintInsets = new Rect();
+ private final Rect mZeroInsets = new Rect(0, 0, 0, 0);
+
// Bounds updated by the evaluator as animator is running.
private final Rect mAnimatedRect = new Rect();
private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
private final RectEvaluator mRectEvaluator;
+ private final RectEvaluator mInsetEvaluator;
private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
public PipEnterExitAnimator(Context context,
@NonNull SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
@NonNull Rect baseBounds,
@NonNull Rect startBounds,
@NonNull Rect endBounds,
- @BOUNDS int direction) {
+ @Nullable Rect sourceRectHint,
+ @BOUNDS int direction,
+ @Surface.Rotation int rotation) {
mLeash = leash;
mStartTransaction = startTransaction;
+ mFinishTransaction = finishTransaction;
mBaseBounds.set(baseBounds);
mStartBounds.set(startBounds);
mAnimatedRect.set(startBounds);
mEndBounds.set(endBounds);
mRectEvaluator = new RectEvaluator(mAnimatedRect);
+ mInsetEvaluator = new RectEvaluator(new Rect());
mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
mDirection = direction;
+ mRotation = rotation;
+
+ mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
+ if (mSourceRectHint != null) {
+ mSourceRectHintInsets.set(
+ mSourceRectHint.left - mBaseBounds.left,
+ mSourceRectHint.top - mBaseBounds.top,
+ mBaseBounds.right - mSourceRectHint.right,
+ mBaseBounds.bottom - mSourceRectHint.bottom
+ );
+ }
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
- mEnterAnimationDuration = context.getResources()
+ mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
- setDuration(mEnterAnimationDuration);
+ setObjectValues(startBounds, endBounds);
+ setDuration(mEnterExitAnimationDuration);
setEvaluator(mRectEvaluator);
addListener(this);
addUpdateListener(this);
@@ -118,6 +143,14 @@ public class PipEnterExitAnimator extends ValueAnimator
@Override
public void onAnimationEnd(@NonNull Animator animation) {
+ if (mFinishTransaction != null) {
+ // finishTransaction might override some state (eg. corner radii) so we want to
+ // manually set the state to the end of the animation
+ mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint,
+ mBaseBounds, mAnimatedRect, getInsets(1f), isInPipDirection(), 1f)
+ .round(mFinishTransaction, mLeash, isInPipDirection())
+ .shadow(mFinishTransaction, mLeash, isInPipDirection());
+ }
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
}
@@ -127,19 +160,32 @@ public class PipEnterExitAnimator extends ValueAnimator
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
final float fraction = getAnimatedFraction();
+ Rect insets = getInsets(fraction);
+
// TODO (b/350801661): implement fixed rotation
- mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, null,
- mBaseBounds, mAnimatedRect, null, isInPipDirection(), fraction)
+ mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
+ mBaseBounds, mAnimatedRect, insets, isInPipDirection(), fraction)
.round(tx, mLeash, isInPipDirection())
.shadow(tx, mLeash, isInPipDirection());
tx.apply();
}
+ private Rect getInsets(float fraction) {
+ Rect startInsets = isInPipDirection() ? mZeroInsets : mSourceRectHintInsets;
+ Rect endInsets = isInPipDirection() ? mSourceRectHintInsets : mZeroInsets;
+
+ return mInsetEvaluator.evaluate(fraction, startInsets, endInsets);
+ }
+
private boolean isInPipDirection() {
return mDirection == BOUNDS_ENTER;
}
+ private boolean isOutPipDirection() {
+ return mDirection == BOUNDS_EXIT;
+ }
+
// no-ops
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
index e04178e6d58cc7ea4ee33645e367566deb663307..b3070f29c6e2ad840f20e2d37076259e514cfc2e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
@@ -35,9 +35,9 @@ import androidx.annotation.NonNull;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 7f168800fb29cf777747cf356af2906651330c5b..262c14d2bfe39193ae8a22fa1085c55e4a24c4f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -25,6 +25,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.view.SurfaceControl;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.util.Preconditions;
@@ -88,6 +89,11 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
: new PictureInPictureParams.Builder().build());
}
+ @NonNull
+ public PictureInPictureParams getPictureInPictureParams() {
+ return mPictureInPictureParams;
+ }
+
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
PictureInPictureParams params = taskInfo.pictureInPictureParams;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 44baabdd5e2e7ada7bb7238252e80947bf403195..f93233ec7461fd0ebd88d45c1ea0895013d09e0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -36,6 +36,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -398,17 +399,22 @@ public class PipTransition extends PipTransitionController implements
SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");
+ Rect sourceRectHint = null;
+ if (pipChange.getTaskInfo() != null
+ && pipChange.getTaskInfo().pictureInPictureParams != null) {
+ sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+ }
+
PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
- startTransaction, startBounds, startBounds, endBounds,
- PipEnterExitAnimator.BOUNDS_ENTER);
+ startTransaction, finishTransaction, startBounds, startBounds, endBounds,
+ sourceRectHint, PipEnterExitAnimator.BOUNDS_ENTER, Surface.ROTATION_0);
tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
this::onClientDrawAtTransitionEnd);
finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
- animator.setAnimationEndCallback(() -> {
- finishCallback.onTransitionFinished(finishWct.isEmpty() ? null : finishWct);
- });
+ animator.setAnimationEndCallback(() ->
+ finishCallback.onTransitionFinished(finishWct));
animator.start();
return true;
@@ -452,19 +458,53 @@ public class PipTransition extends PipTransitionController implements
TransitionInfo.Change pipChange = getChangeByToken(info, pipToken);
if (pipChange == null) {
- return false;
+ // pipChange is null, check to see if we've reparented the PIP activity for
+ // the multi activity case. If so we should use the activity leash instead
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() == null
+ && change.getLastParent() != null
+ && change.getLastParent().equals(pipToken)) {
+ pipChange = change;
+ break;
+ }
+ }
+
+ // failsafe
+ if (pipChange == null) {
+ return false;
+ }
+ }
+
+ // for multi activity, we need to manually set the leash layer
+ if (pipChange.getTaskInfo() == null) {
+ TransitionInfo.Change parent = getChangeByToken(info, pipChange.getParent());
+ if (parent != null) {
+ startTransaction.setLayer(parent.getLeash(), Integer.MAX_VALUE - 1);
+ }
}
Rect startBounds = pipChange.getStartAbsBounds();
Rect endBounds = pipChange.getEndAbsBounds();
SurfaceControl pipLeash = pipChange.getLeash();
+ Preconditions.checkNotNull(pipLeash, "Leash is null for exit transition.");
+
+ Rect sourceRectHint = null;
+ if (pipChange.getTaskInfo() != null
+ && pipChange.getTaskInfo().pictureInPictureParams != null) {
+ // single activity
+ sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+ } else if (mPipTaskListener.getPictureInPictureParams().hasSourceBoundsHint()) {
+ // multi activity
+ sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
+ }
PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
- startTransaction, startBounds, startBounds, endBounds,
- PipEnterExitAnimator.BOUNDS_EXIT);
+ startTransaction, finishTransaction, endBounds, startBounds, endBounds,
+ sourceRectHint, PipEnterExitAnimator.BOUNDS_EXIT, Surface.ROTATION_0);
+
animator.setAnimationEndCallback(() -> {
- finishCallback.onTransitionFinished(null);
mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
+ finishCallback.onTransitionFinished(null);
});
animator.start();
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 39bea1bed447e89662350ddff231e9a93233385a..9af33a84b0458bb738ba9e39eb4d6e23065a09f2 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
@@ -21,9 +21,12 @@ import static android.content.pm.PackageManager.FEATURE_PC;
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
+import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -158,6 +161,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
return new IRecentTasksImpl(this);
}
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
private void onInit() {
mShellController.addExternalInterface(KEY_EXTRA_SHELL_RECENT_TASKS,
this::createExternalInterface, this);
@@ -168,6 +172,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
mMainExecutor);
}
+ mContext.getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
+ mMainExecutor, isKeyguardLocked -> notifyRecentTasksChanged());
}
void setTransitionHandler(RecentsTransitionHandler handler) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
deleted file mode 100644
index 1cbb8bbe5f75209f4328932fe3af1079a502b90f..0000000000000000000000000000000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
-
-import android.content.Context;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.protolog.ProtoLog;
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-
-import java.util.Optional;
-
-/**
- * Main stage for split-screen mode. When split-screen is active all standard activity types launch
- * on the main stage, except for task that are explicitly pinned to the {@link SideStage}.
- * @see StageCoordinator
- */
-class MainStage extends StageTaskListener {
- private boolean mIsActive = false;
-
- MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- Optional windowDecorViewModel) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- iconProvider, windowDecorViewModel);
- }
-
- boolean isActive() {
- return mIsActive;
- }
-
- void activate(WindowContainerTransaction wct, boolean includingTopTask) {
- if (mIsActive) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b",
- includingTopTask);
-
- if (includingTopTask) {
- reparentTopTask(wct);
- }
-
- mIsActive = true;
- }
-
- void deactivate(WindowContainerTransaction wct) {
- deactivate(wct, false /* toTop */);
- }
-
- void deactivate(WindowContainerTransaction wct, boolean toTop) {
- if (!mIsActive) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s",
- toTop, mRootTaskInfo);
- mIsActive = false;
-
- if (mRootTaskInfo == null) return;
- final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.reparentTasks(
- rootToken,
- null /* newParent */,
- null /* windowingModes */,
- null /* activityTypes */,
- toTop);
- }
-}
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
deleted file mode 100644
index 27fd309c09b08e0d44b7a332569083c58c12bc94..0000000000000000000000000000000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.protolog.ProtoLog;
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-
-import java.util.Optional;
-
-/**
- * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up
- * here. All other task are launch in the {@link MainStage}.
- *
- * @see StageCoordinator
- */
-class SideStage extends StageTaskListener {
- private static final String TAG = SideStage.class.getSimpleName();
-
- SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- Optional windowDecorViewModel) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- iconProvider, windowDecorViewModel);
- }
-
- boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
- mChildrenTaskInfo.size(), toTop);
- if (mChildrenTaskInfo.size() == 0) return false;
- wct.reparentTasks(
- mRootTaskInfo.token,
- null /* newParent */,
- null /* windowingModes */,
- null /* activityTypes */,
- toTop);
- return true;
- }
-
- boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
- final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
- task != null);
- if (task == null) return false;
- wct.reparent(task.token, newParent, false /* onTop */);
- return true;
- }
-}
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 a6233dc927a56ecf08da9ab101da194bb955e468..b36b1f84d21f841344bced88529dba2f690c94cb 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
@@ -44,13 +44,13 @@ public interface SplitScreen {
int STAGE_TYPE_UNDEFINED = -1;
/**
* The main stage type.
- * @see MainStage
+ * @see StageTaskListener
*/
int STAGE_TYPE_MAIN = 0;
/**
* The side stage type.
- * @see SideStage
+ * @see StageTaskListener
*/
int STAGE_TYPE_SIDE = 1;
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 27ded57b38d91d9b0718d1f802d0176435f700bf..1e6fa2807b9b48edc3ac497843c3ae0f5a0c348f 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
@@ -32,6 +32,7 @@ import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED_
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.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_DRAG;
@@ -57,6 +58,7 @@ import android.util.Slog;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
@@ -133,6 +135,11 @@ public class SplitscreenEventLogger {
@SplitPosition int mainStagePosition, int mainStageUid,
@SplitPosition int sideStagePosition, int sideStageUid,
boolean isLandscape) {
+ if (hasStartedSession()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logEnter: no-op, previous session has not ended");
+ return;
+ }
+
mLoggerSessionId = mIdSequence.newInstanceId();
int enterReason = getLoggerEnterReason(isLandscape);
updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
@@ -140,6 +147,14 @@ public class SplitscreenEventLogger {
updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
sideStageUid);
updateSplitRatioState(splitRatio);
+
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logEnter: enterReason=%d splitRatio=%f "
+ + "mainStagePosition=%d mainStageUid=%d sideStagePosition=%d "
+ + "sideStageUid=%d isLandscape=%b mEnterSessionId=%d mLoggerSessionId=%d",
+ enterReason, splitRatio, mLastMainStagePosition, mLastMainStageUid,
+ mLastSideStagePosition, mLastSideStageUid, isLandscape,
+ mEnterSessionId != null ? mEnterSessionId.getId() : 0, mLoggerSessionId.getId());
+
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER,
enterReason,
@@ -212,14 +227,25 @@ public class SplitscreenEventLogger {
@SplitPosition int mainStagePosition, int mainStageUid,
@SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
if (mLoggerSessionId == null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logExit: no-op, mLoggerSessionId is null");
// Ignore changes until we've started logging the session
return;
}
if ((mainStagePosition != SPLIT_POSITION_UNDEFINED
&& sideStagePosition != SPLIT_POSITION_UNDEFINED)
|| (mainStageUid != 0 && sideStageUid != 0)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "logExit: no-op, only main or side stage should be set, not both/none");
throw new IllegalArgumentException("Only main or side stage should be set");
}
+
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logExit: exitReason=%d mainStagePosition=%d"
+ + " mainStageUid=%d sideStagePosition=%d sideStageUid=%d isLandscape=%b"
+ + " mLoggerSessionId=%d", getLoggerExitReason(exitReason),
+ getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), mainStageUid,
+ getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), sideStageUid,
+ isLandscape, mLoggerSessionId.getId());
+
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT,
0 /* enterReason */,
@@ -304,25 +330,34 @@ public class SplitscreenEventLogger {
*/
public void logResize(float splitRatio) {
if (mLoggerSessionId == null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: no-op, mLoggerSessionId is null");
// Ignore changes until we've started logging the session
return;
}
if (splitRatio <= 0f || splitRatio >= 1f) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "logResize: no-op, splitRatio indicates that user is dismissing, not resizing");
// Don't bother reporting resizes that end up dismissing the split, that will be logged
// via the exit event
return;
}
if (!updateSplitRatioState(splitRatio)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: no-op, split ratio was not changed");
// Ignore if there are no user perceived changes
return;
}
+
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: splitRatio=%f mLoggerSessionId=%d",
+ mLastSplitRatio, mLoggerSessionId.getId());
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE,
0 /* enterReason */,
0 /* exitReason */,
mLastSplitRatio,
- 0 /* mainStagePosition */, 0 /* mainStageUid */,
- 0 /* sideStagePosition */, 0 /* sideStageUid */,
+ mLastMainStagePosition,
+ mLastMainStageUid,
+ mLastSideStagePosition,
+ mLastSideStageUid,
0 /* dragInstanceId */,
mLoggerSessionId.getId());
}
@@ -333,6 +368,7 @@ public class SplitscreenEventLogger {
public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid,
@SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
if (mLoggerSessionId == null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logSwap: no-op, mLoggerSessionId is null");
// Ignore changes until we've started logging the session
return;
}
@@ -341,6 +377,11 @@ public class SplitscreenEventLogger {
mainStageUid);
updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
sideStageUid);
+
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logSwap: mainStagePosition=%d mainStageUid=%d "
+ + "sideStagePosition=%d sideStageUid=%d mLoggerSessionId=%d",
+ mLastMainStagePosition, mLastMainStageUid, mLastSideStagePosition,
+ mLastSideStageUid, mLoggerSessionId.getId());
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP,
0 /* enterReason */,
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 95f864a775be2ecfcfe365f55170185dda0aba1c..f3113dce94a47220db63649598e6d4af6520a5b9 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
@@ -21,6 +21,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -33,6 +34,7 @@ import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
@@ -153,14 +155,12 @@ import java.util.Set;
import java.util.concurrent.Executor;
/**
- * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
- * {@link SideStage} stages.
+ * Coordinates the staging (visibility, sizing, ...) of the split-screen stages.
* Some high-level rules:
- * - The {@link StageCoordinator} is only considered active if the {@link SideStage} contains at
+ * - The {@link StageCoordinator} is only considered active if the other stages contain at
* least one child task.
- * - The {@link MainStage} should only have children if the coordinator is active.
- * - The {@link SplitLayout} divider is only visible if both the {@link MainStage}
- * and {@link SideStage} are visible.
+ * - The {@link SplitLayout} divider is only visible if multiple {@link StageTaskListener}s are
+ * visible
* - Both stages are put under a single-top root task.
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
@@ -173,9 +173,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final SurfaceSession mSurfaceSession = new SurfaceSession();
- private final MainStage mMainStage;
+ private final StageTaskListener mMainStage;
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
- private final SideStage mSideStage;
+ private final StageTaskListener mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -328,7 +328,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
- mMainStage = new MainStage(
+ mMainStage = new StageTaskListener(
mContext,
mTaskOrganizer,
mDisplayId,
@@ -337,7 +337,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSurfaceSession,
iconProvider,
mWindowDecorViewModel);
- mSideStage = new SideStage(
+ mSideStage = new StageTaskListener(
mContext,
mTaskOrganizer,
mDisplayId,
@@ -366,8 +366,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@VisibleForTesting
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage,
- DisplayController displayController, DisplayImeController displayImeController,
+ ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
+ StageTaskListener sideStage, DisplayController displayController,
+ DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
Handler mainHandler, Optional recentTasks,
@@ -419,10 +420,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return mSideStageListener.mVisible && mMainStageListener.mVisible;
}
+ private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) {
+ mMainStage.activate(wct, includingTopTask);
+ }
+
public boolean isSplitActive() {
return mMainStage.isActive();
}
+ /**
+ * Deactivates main stage by removing the stage from the top level split root (usually when a
+ * task underneath gets removed from the stage root).
+ * @param reparentToTop whether we want to put the stage root back on top
+ */
+ private void deactivateSplit(WindowContainerTransaction wct, boolean reparentToTop) {
+ mMainStage.deactivate(wct, reparentToTop);
+ }
+
/** @return whether this transition-request has the launch-adjacent flag. */
public boolean requestHasLaunchAdjacentFlag(TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
@@ -495,12 +509,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId);
final WindowContainerTransaction wct = new WindowContainerTransaction();
- /**
- * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the
- * {@link SideStage} no longer has children.
- */
+
+ // MainStage will be deactivated in onStageHasChildrenChanged() if the other stages
+ // no longer have children.
+
final boolean result = mSideStage.removeTask(taskId,
- mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null,
+ isSplitActive() ? mMainStage.mRootTaskInfo.token : null,
wct);
mTaskOrganizer.applyTransaction(wct);
return result;
@@ -617,7 +631,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
// If split screen is not activated, we're expecting to open a pair of apps to split.
- final int extraTransitType = mMainStage.isActive()
+ final int extraTransitType = isSplitActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);
@@ -660,7 +674,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
// If split screen is not activated, we're expecting to open a pair of apps to split.
- final int extraTransitType = mMainStage.isActive()
+ final int extraTransitType = isSplitActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);
@@ -684,6 +698,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
+ prepareTasksForSplitScreen(new int[] {taskId1, taskId2}, wct);
wct.startTask(taskId1, options1);
startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId);
@@ -714,6 +729,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+ prepareTasksForSplitScreen(new int[] {taskId}, wct);
startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
}
@@ -757,10 +773,29 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+ prepareTasksForSplitScreen(new int[] {taskId}, wct);
startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
}
+ /**
+ * Prepares the tasks whose IDs are provided in `taskIds` for split screen by clearing their
+ * bounds and windowing mode so that they can inherit the bounds and the windowing mode of
+ * their root stages.
+ *
+ * @param taskIds an array of task IDs whose bounds will be cleared.
+ * @param wct transaction to clear the bounds on the tasks.
+ */
+ private void prepareTasksForSplitScreen(int[] taskIds, WindowContainerTransaction wct) {
+ for (int taskId : taskIds) {
+ ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
+ if (task != null) {
+ wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
+ .setBounds(task.token, null);
+ }
+ }
+ }
+
/**
* Starts with the second task to a split pair in one transition.
*
@@ -771,10 +806,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
@Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
- if (!mMainStage.isActive()) {
+ if (!isSplitActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- mMainStage.activate(wct, false /* reparent */);
+ activateSplit(wct, false /* reparentToTop */);
}
mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
@@ -838,10 +873,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return;
}
- if (!mMainStage.isActive()) {
+ if (!isSplitActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- mMainStage.activate(wct, false /* reparent */);
+ activateSplit(wct, false /* reparentToTop */);
}
setSideStagePosition(splitPosition, wct);
@@ -1088,7 +1123,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
*/
void onKeyguardStateChanged(boolean active, boolean occludingTaskRunning) {
mKeyguardActive = active;
- if (!mMainStage.isActive()) {
+ if (!isSplitActive()) {
return;
}
ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
@@ -1132,7 +1167,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
* will do a no-op.
*/
void dismissSplitKeepingLastActiveStage(@ExitReason int reason) {
- if (!mMainStage.isActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) {
+ if (!isSplitActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) {
// no-op
return;
}
@@ -1155,8 +1190,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void exitSplitScreen(@Nullable StageTaskListener childrenToTop,
@ExitReason int exitReason) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b",
- childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive());
- if (!mMainStage.isActive()) return;
+ childrenToTop == mMainStage, exitReasonToString(exitReason), isSplitActive());
+ if (!isSplitActive()) return;
final WindowContainerTransaction wct = new WindowContainerTransaction();
applyExitSplitScreen(childrenToTop, wct, exitReason);
@@ -1166,7 +1201,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
WindowContainerTransaction wct, @ExitReason int exitReason) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s",
exitReasonToString(exitReason));
- if (!mMainStage.isActive() || mIsExiting) return;
+ if (!isSplitActive() || mIsExiting) return;
onSplitScreenExit();
clearSplitPairedInRecents(exitReason);
@@ -1178,7 +1213,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.getInvisibleBounds(mTempRect1);
if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
mSideStage.removeAllTasks(wct, false /* toTop */);
- mMainStage.deactivate(wct, false /* toTop */);
+ deactivateSplit(wct, false /* reparentToTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
setRootForceTranslucent(true, wct);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
@@ -1207,7 +1242,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
childrenToTop.fadeOutDecor(() -> {
WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false;
- mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
+ deactivateSplit(finishedWCT, childrenToTop == mMainStage /* reparentToTop */);
mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
setRootForceTranslucent(true, finishedWCT);
@@ -1230,7 +1265,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
void dismissSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
- if (!mMainStage.isActive()) return;
+ if (!isSplitActive()) return;
final int stage = getStageOfTask(toTopTaskId);
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(stage, wct);
@@ -1340,10 +1375,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
*/
void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
- if (!mMainStage.isActive()) return;
+ if (!isSplitActive()) return;
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop);
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
- mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
+ deactivateSplit(wct, stageToTop == STAGE_TYPE_MAIN);
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
@@ -1408,7 +1443,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setSideStagePosition(startPosition, wct);
mSideStage.addTask(taskInfo, wct);
}
- mMainStage.activate(wct, true /* includingTopTask */);
+ activateSplit(wct, true /* reparentToTop */);
prepareSplitLayout(wct, resizeAnim);
}
@@ -1449,12 +1484,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSkipEvictingMainStageChildren = false;
mSplitRequest = null;
updateRecentTasksSplitPair();
- if (!mLogger.hasStartedSession()) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLeftRightSplit());
- }
+
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLeftRightSplit());
}
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
@@ -1640,7 +1674,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRootTaskInfo = taskInfo;
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
- && mMainStage.isActive()) {
+ && isSplitActive()) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating",
taskInfo.taskId);
// Clear the divider remote animating flag as the divider will be re-rendered to apply
@@ -1894,7 +1928,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
stageListener == mMainStageListener);
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
- if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
+ if (!hasChildren && !mIsExiting && isSplitActive()) {
if (isSideStage && mMainStageListener.mVisible) {
// Exit to main stage if side stage no longer has children.
mSplitLayout.flingDividerToDismiss(
@@ -1909,7 +1943,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Dismiss split screen in the background once any sides of the split become empty.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
}
- } else if (isSideStage && hasChildren && !mMainStage.isActive()) {
+ } else if (isSideStage && hasChildren && !isSplitActive()) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareEnterSplitScreen(wct);
@@ -1930,15 +1964,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
clearRequestIfPresented();
updateRecentTasksSplitPair();
- if (!mLogger.hasStartedSession()) {
- if (!mLogger.hasValidEnterSessionId()) {
- mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE);
- }
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLeftRightSplit());
+ if (!mLogger.hasStartedSession() && !mLogger.hasValidEnterSessionId()) {
+ mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE);
}
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLeftRightSplit());
}
}
@@ -2124,7 +2156,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
- if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) {
+ if (displayId != DEFAULT_DISPLAY || !isSplitActive()) {
return;
}
@@ -2419,7 +2451,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Not entering or exiting, so just do some house-keeping and validation.
// If we're not in split-mode, just abort so something else can handle it.
- if (!mMainStage.isActive()) return false;
+ if (!isSplitActive()) return false;
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId());
mSplitLayout.setFreezeDividerWindow(false);
@@ -2661,7 +2693,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void onTransitionAnimationComplete() {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete");
// If still playing, let it finish.
- if (!mMainStage.isActive() && !mIsExiting) {
+ if (!isSplitActive() && !mIsExiting) {
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
mSplitLayout.release();
@@ -3125,7 +3157,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ (mSplitLayout != null ? mSplitLayout.isLeftRightSplit() : "null"));
pw.println(innerPrefix + "MainStage");
pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition()));
- pw.println(childPrefix + "isActive=" + mMainStage.isActive());
+ pw.println(childPrefix + "isActive=" + isSplitActive());
mMainStage.dump(pw, childPrefix);
pw.println(innerPrefix + "MainStageListener");
mMainStageListener.dump(pw, childPrefix);
@@ -3245,7 +3277,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
- if (mMainStage.isActive()) {
+ if (isSplitActive()) {
final boolean isMainStage = mMainStageListener == this;
// If visible, we preserve the app and keep it running. If an app becomes
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 99f3832192e096d80861458ac79920948d6e0259..29b5114d87e6718f1e15d8a9512f03deb6b282e8 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
@@ -72,6 +72,10 @@ import java.util.function.Predicate;
public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = StageTaskListener.class.getSimpleName();
+ // No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the
+ // stages should have this be set/being used
+ private boolean mIsActive;
+
/** Callback interface for listening to changes in a split-screen stage. */
public interface StageListenerCallbacks {
void onRootTaskAppeared();
@@ -475,6 +479,68 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
});
}
+ // ---------
+ // Previously only used in MainStage
+ boolean isActive() {
+ return mIsActive;
+ }
+
+ void activate(WindowContainerTransaction wct, boolean includingTopTask) {
+ if (mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b",
+ includingTopTask);
+
+ if (includingTopTask) {
+ reparentTopTask(wct);
+ }
+
+ mIsActive = true;
+ }
+
+ void deactivate(WindowContainerTransaction wct) {
+ deactivate(wct, false /* toTop */);
+ }
+
+ void deactivate(WindowContainerTransaction wct, boolean toTop) {
+ if (!mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: toTop=%b rootTaskInfo=%s",
+ toTop, mRootTaskInfo);
+ mIsActive = false;
+
+ if (mRootTaskInfo == null) return;
+ final WindowContainerToken rootToken = mRootTaskInfo.token;
+ wct.reparentTasks(
+ rootToken,
+ null /* newParent */,
+ null /* windowingModes */,
+ null /* activityTypes */,
+ toTop);
+ }
+
+ // --------
+ // Previously only used in SideStage
+ boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
+ mChildrenTaskInfo.size(), toTop);
+ if (mChildrenTaskInfo.size() == 0) return false;
+ wct.reparentTasks(
+ mRootTaskInfo.token,
+ null /* newParent */,
+ null /* windowingModes */,
+ null /* activityTypes */,
+ toTop);
+ return true;
+ }
+
+ boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
+ final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
+ task != null);
+ if (task == null) return false;
+ wct.reparent(task.token, newParent, false /* onTop */);
+ return true;
+ }
+
private void sendStatusChanged() {
mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index b18feefe7eb3c2c04d7af1f148532a63a3ac134e..81f444ba2af3b73fe86b5bba130e5b3b2372e806 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -352,12 +352,17 @@ public class SplashscreenContentDrawer {
/** Extract the window background color from {@code attrs}. */
private static int peekWindowBGColor(Context context, SplashScreenWindowAttrs attrs) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "peekWindowBGColor");
- final Drawable themeBGDrawable;
+ Drawable themeBGDrawable = null;
if (attrs.mWindowBgColor != 0) {
themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor);
} else if (attrs.mWindowBgResId != 0) {
- themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
- } else {
+ try {
+ themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
+ } catch (Resources.NotFoundException e) {
+ Slog.w(TAG, "Unable get drawable from resource", e);
+ }
+ }
+ if (themeBGDrawable == null) {
themeBGDrawable = createDefaultBackgroundDrawable();
Slog.w(TAG, "Window background does not exist, using " + themeBGDrawable);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index a85188a9e04d0899b5c7b568d2e29830f90f0e80..82c0aaf3bc8b81b58fde873ea4d3b0d7a781b8f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -118,6 +118,13 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
mTaskViewTaskController.startShortcutActivity(shortcut, options, launchBounds);
}
+ /**
+ * Moves the current task in taskview out of the view and back to fullscreen.
+ */
+ public void moveToFullscreen() {
+ mTaskViewTaskController.moveToFullscreen();
+ }
+
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
if (mTaskViewTaskController.isUsingShellTransitions()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 9750d3ec99f5ccb114de71ade7795c8654e7b501..e74342e1910cb0556861c8170b95ea23af9b2ae6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.taskview;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import android.annotation.NonNull;
@@ -256,6 +257,24 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
mTaskViewTransitions.startInstantTransition(TRANSIT_CHANGE, wct);
}
+ /**
+ * Moves the current task in TaskView out of the view and back to fullscreen.
+ */
+ public void moveToFullscreen() {
+ if (mTaskToken == null) return;
+ mShellExecutor.execute(() -> {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setWindowingMode(mTaskToken, WINDOWING_MODE_UNDEFINED);
+ wct.setAlwaysOnTop(mTaskToken, false);
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+ mTaskViewTransitions.moveTaskViewToFullscreen(wct, this);
+ if (mListener != null) {
+ // Task is being "removed" from the clients perspective
+ mListener.onTaskRemovalStarted(mTaskInfo.taskId);
+ }
+ });
+ }
+
private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
final Binder launchCookie = new Binder();
mShellExecutor.execute(() -> {
@@ -585,7 +604,6 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
});
}
mTaskViewBase.onTaskVanished(taskInfo);
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, false);
}
}
@@ -699,6 +717,9 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
mTaskViewBase.setResizeBgColor(startTransaction, backgroundColor);
}
+ // After the embedded task has appeared, set it to non-trimmable. This is important
+ // to prevent recents from trimming and removing the embedded task.
+ wct.setTaskTrimmableFromRecents(taskInfo.token, false /* isTrimmableFromRecents */);
mTaskViewBase.onTaskAppeared(mTaskInfo, mTaskLeash);
if (mListener != null) {
final int taskId = mTaskInfo.taskId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 15fe7abb96a5634a847a0114cfad8c419a884dd2..39648f65b4f3e066b82616b5799e907ff4b51f61 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -236,6 +236,12 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
startNextTransition();
}
+ void moveTaskViewToFullscreen(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskViewTaskController taskView) {
+ mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */));
+ startNextTransition();
+ }
+
/** Starts a new transition to make the given {@code taskView} visible. */
public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
setTaskViewVisible(taskView, visible, false /* reorder */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 9b27e413b5e4ec49d31b55d8cf38f6d1c9ea9486..3d79a1c8cebe954c0207a26a585e7b6ed9cbe7d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -30,6 +30,7 @@ import android.os.IBinder;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
+import com.android.window.flags.Flags;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -71,9 +72,17 @@ public class HomeTransitionObserver implements TransitionObserver,
final int mode = change.getMode();
final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
- if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME
- && (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture)) {
- notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode) || isBackGesture);
+ if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+ if (Flags.migratePredictiveBackTransition()) {
+ if (!isBackGesture && TransitionUtil.isOpenOrCloseMode(mode)) {
+ notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode));
+ }
+ } else {
+ if (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture) {
+ notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode)
+ || isBackGesture);
+ }
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 1f95667f4e35c4ad1185e8048cf1dc41f43413c1..79190689adc10a164096ea143cf1c7802253a3c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -90,6 +90,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -97,6 +98,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
@@ -166,6 +168,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private TaskOperations mTaskOperations;
private final Supplier mTransactionFactory;
private final Transitions mTransitions;
+ private final Optional
+ mActivityOrientationChangeHandler;
private SplitScreenController mSplitScreenController;
@@ -179,6 +183,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final Region mExclusionRegion = Region.obtain();
private boolean mInImmersiveMode;
private final String mSysUIPackageName;
+ private final AssistContentRequester mAssistContentRequester;
private final DisplayChangeController.OnDisplayChangingListener mOnDisplayChangingListener;
private final ISystemGestureExclusionListener mGestureExclusionListener =
@@ -214,8 +219,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
- Optional desktopTasksLimiter
+ Optional desktopTasksLimiter,
+ Optional activityOrientationChangeHandler
) {
this(
context,
@@ -234,6 +241,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
transitions,
desktopTasksController,
genericLinksParser,
+ assistContentRequester,
multiInstanceHelper,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
@@ -241,7 +249,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
rootTaskDisplayAreaOrganizer,
new SparseArray<>(),
interactionJankMonitor,
- desktopTasksLimiter);
+ desktopTasksLimiter,
+ activityOrientationChangeHandler);
}
@VisibleForTesting
@@ -262,6 +271,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
Transitions transitions,
Optional desktopTasksController,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
@@ -269,7 +279,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SparseArray windowDecorByTaskId,
InteractionJankMonitor interactionJankMonitor,
- Optional desktopTasksLimiter) {
+ Optional desktopTasksLimiter,
+ Optional activityOrientationChangeHandler) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -297,6 +308,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
com.android.internal.R.string.config_systemUi);
mInteractionJankMonitor = interactionJankMonitor;
mDesktopTasksLimiter = desktopTasksLimiter;
+ mActivityOrientationChangeHandler = activityOrientationChangeHandler;
+ mAssistContentRequester = assistContentRequester;
mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
DesktopModeWindowDecoration decoration;
RunningTaskInfo taskInfo;
@@ -388,6 +401,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
incrementEventReceiverTasks(taskInfo.displayId);
}
decoration.relayout(taskInfo);
+ mActivityOrientationChangeHandler.ifPresent(handler ->
+ handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
}
@Override
@@ -482,7 +497,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} else {
mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,
Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable");
- mDesktopTasksController.snapToHalfScreen(decoration.mTaskInfo,
+ mDesktopTasksController.snapToHalfScreen(
+ decoration.mTaskInfo,
+ decoration.mTaskSurface,
decoration.mTaskInfo.configuration.windowConfiguration.getBounds(),
left ? SnapPosition.LEFT : SnapPosition.RIGHT);
}
@@ -496,16 +513,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
if (decoration == null) {
return;
}
- openInBrowser(uri);
+ openInBrowser(uri, decoration.getUser());
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
}
- private void openInBrowser(Uri uri) {
+ private void openInBrowser(Uri uri, @NonNull UserHandle userHandle) {
final Intent intent = Intent.makeMainSelectorActivity(ACTION_MAIN, CATEGORY_APP_BROWSER)
.setData(uri)
.addFlags(FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
+ mContext.startActivityAsUser(intent, userHandle);
}
private void onToDesktop(int taskId, DesktopModeTransitionSource source) {
@@ -615,7 +632,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
if (!decoration.isHandleMenuActive()) {
moveTaskToFront(decoration.mTaskInfo);
- decoration.createHandleMenu(mSplitScreenController);
+ decoration.createHandleMenu();
}
} else if (id == R.id.maximize_window) {
// TODO(b/346441962): move click detection logic into the decor's
@@ -1259,6 +1276,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mSyncQueue,
mRootTaskDisplayAreaOrganizer,
mGenericLinksParser,
+ mAssistContentRequester,
mMultiInstanceHelper);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 2bec3fa6418da3eaf117690b8b2800d2b9170916..142be91fe942f9cd6a0951f922b80c84be790baa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -38,6 +38,7 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
+import android.app.assist.AssistContent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -54,6 +55,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Trace;
+import android.os.UserHandle;
import android.util.Size;
import android.util.Slog;
import android.view.Choreographer;
@@ -74,6 +76,8 @@ import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AppToWebUtils;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.MultiInstanceHelper;
@@ -113,6 +117,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration mOpenInBrowserClickListener;
private ExclusionRegionListener mExclusionRegionListener;
@@ -156,6 +162,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration surfaceControlBuilderSupplier,
Supplier surfaceControlTransactionSupplier,
Supplier windowContainerTransactionSupplier,
Supplier surfaceControlSupplier,
+ WindowManagerWrapper windowManagerWrapper,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
MaximizeMenuFactory maximizeMenuFactory,
HandleMenuFactory handleMenuFactory,
@@ -225,9 +236,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration {
+ mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
return Unit.INSTANCE;
},
@@ -1321,6 +1363,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration= SystemBarUtils.getStatusBarHeight(context)) return
+ if (!isCaptionVisible && hasStatusBarInputLayer() ) {
disposeStatusBarInputLayer()
return
}
@@ -96,11 +101,12 @@ internal class AppHandleViewHolder(
handleWidth: Int,
handleHeight: Int) {
if (!Flags.enableAdditionalWindowsAboveStatusBar()) return
- statusBarInputLayer = AdditionalSystemViewContainer(context, taskInfo.taskId,
- handlePosition.x, handlePosition.y, handleWidth, handleHeight,
+ statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper,
+ taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
- val view = statusBarInputLayer?.view
- val lp = view?.layoutParams as WindowManager.LayoutParams
+ val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View")
+ val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer" +
+ "LayoutParams")
lp.title = "Handle Input Layer of task " + taskInfo.taskId
lp.setTrustedOverlay()
// Make this window a spy window to enable it to pilfer pointers from the system-wide
@@ -118,9 +124,9 @@ internal class AppHandleViewHolder(
inputManager.pilferPointers(v.viewRootImpl.inputToken)
}
captionHandle.dispatchTouchEvent(event)
- true
+ return@setOnTouchListener true
}
- windowManager.updateViewLayout(view, lp)
+ windowManagerWrapper.updateViewLayout(view, lp)
}
private fun updateStatusBarInputLayer(globalPosition: Point) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 3fb67cd522c0166b642d7e4791a6656c37ab946d..7640cb1fb6163e877a266121856bbf290fd2f171 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -16,13 +16,23 @@
package com.android.wm.shell.flicker
+import android.tools.PlatformConsts.DESKTOP_MODE_MINIMUM_WINDOW_HEIGHT
+import android.tools.PlatformConsts.DESKTOP_MODE_MINIMUM_WINDOW_WIDTH
import android.tools.flicker.AssertionInvocationGroup
+import android.tools.flicker.assertors.assertions.AppLayerIncreasesInSize
import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible
+import android.tools.flicker.assertors.assertions.AppWindowAlignsWithOnlyOneDisplayCornerAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowCoversRightHalfScreenAtEnd
import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
+import android.tools.flicker.assertors.assertions.AppWindowHasMaxBoundsInOnlyOneDimension
+import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayHeight
+import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayWidth
import android.tools.flicker.assertors.assertions.AppWindowHasSizeOfAtLeast
+import android.tools.flicker.assertors.assertions.AppWindowInsideDisplayBoundsAtEnd
import android.tools.flicker.assertors.assertions.AppWindowIsInvisibleAtEnd
import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppWindowMaintainsAspectRatioAlways
@@ -51,21 +61,21 @@ class DesktopModeFlickerScenarios {
FlickerConfigEntry(
scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"),
extractor =
- ShellTransitionScenarioExtractor(
- transitionMatcher =
- object : ITransitionMatcher {
- override fun findAll(
- transitions: Collection
- ): Collection {
- return transitions.filter {
- // TODO(351168217) Use jank CUJ to extract a longer trace
- it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP
- }
- }
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection
+ ): Collection {
+ return transitions.filter {
+ // TODO(351168217) Use jank CUJ to extract a longer trace
+ it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP
}
- ),
+ }
+ }
+ ),
assertions =
- AssertionTemplates.COMMON_ASSERTIONS +
+ AssertionTemplates.COMMON_ASSERTIONS +
listOf(
AppLayerIsVisibleAlways(DESKTOP_MODE_APP),
AppWindowOnTopAtEnd(DESKTOP_MODE_APP),
@@ -81,24 +91,24 @@ class DesktopModeFlickerScenarios {
FlickerConfigEntry(
scenarioId = ScenarioId("CLOSE_APP"),
extractor =
- ShellTransitionScenarioExtractor(
- transitionMatcher =
- object : ITransitionMatcher {
- override fun findAll(
- transitions: Collection
- ): Collection {
- // In case there are multiple windows closing, filter out the
- // last window closing. It should use the CLOSE_LAST_APP
- // scenario below.
- return transitions
- .filter { it.type == TransitionType.CLOSE }
- .sortedByDescending { it.id }
- .drop(1)
- }
- }
- ),
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection
+ ): Collection {
+ // In case there are multiple windows closing, filter out the
+ // last window closing. It should use the CLOSE_LAST_APP
+ // scenario below.
+ return transitions
+ .filter { it.type == TransitionType.CLOSE }
+ .sortedByDescending { it.id }
+ .drop(1)
+ }
+ }
+ ),
assertions =
- AssertionTemplates.COMMON_ASSERTIONS +
+ AssertionTemplates.COMMON_ASSERTIONS +
listOf(
AppWindowOnTopAtStart(DESKTOP_MODE_APP),
AppLayerIsVisibleAtStart(DESKTOP_MODE_APP),
@@ -110,22 +120,22 @@ class DesktopModeFlickerScenarios {
FlickerConfigEntry(
scenarioId = ScenarioId("CLOSE_LAST_APP"),
extractor =
- ShellTransitionScenarioExtractor(
- transitionMatcher =
- object : ITransitionMatcher {
- override fun findAll(
- transitions: Collection
- ): Collection {
- val lastTransition =
- transitions
- .filter { it.type == TransitionType.CLOSE }
- .maxByOrNull { it.id }!!
- return listOf(lastTransition)
- }
- }
- ),
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection
+ ): Collection {
+ val lastTransition =
+ transitions
+ .filter { it.type == TransitionType.CLOSE }
+ .maxByOrNull { it.id }!!
+ return listOf(lastTransition)
+ }
+ }
+ ),
assertions =
- AssertionTemplates.COMMON_ASSERTIONS +
+ AssertionTemplates.COMMON_ASSERTIONS +
listOf(
AppWindowIsInvisibleAtEnd(DESKTOP_MODE_APP),
LauncherWindowReplacesAppAsTopWindow(DESKTOP_MODE_APP),
@@ -138,28 +148,129 @@ class DesktopModeFlickerScenarios {
FlickerConfigEntry(
scenarioId = ScenarioId("CORNER_RESIZE"),
extractor =
- TaggedScenarioExtractorBuilder()
- .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
- .setTransitionMatcher(
- TaggedCujTransitionMatcher(associatedTransitionRequired = false)
- )
- .build(),
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS
)
+ val EDGE_RESIZE =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("EDGE_RESIZE"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
val CORNER_RESIZE_TO_MINIMUM_SIZE =
FlickerConfigEntry(
scenarioId = ScenarioId("CORNER_RESIZE_TO_MINIMUM_SIZE"),
extractor =
- TaggedScenarioExtractorBuilder()
- .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
- .setTransitionMatcher(
- TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions =
+ AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ AppWindowHasSizeOfAtLeast(
+ DESKTOP_MODE_APP,
+ DESKTOP_MODE_MINIMUM_WINDOW_WIDTH,
+ DESKTOP_MODE_MINIMUM_WINDOW_HEIGHT
+ )
)
- .build(),
+ .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val CORNER_RESIZE_TO_MAXIMUM_SIZE =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("CORNER_RESIZE_TO_MAXIMUM_SIZE"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
assertions =
AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
- listOf(AppWindowHasSizeOfAtLeast(DESKTOP_MODE_APP, 770, 700))
+ listOf(
+ AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP),
+ AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val SNAP_RESIZE_LEFT_WITH_BUTTON =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_BUTTON"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP))
+ .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val SNAP_RESIZE_RIGHT_WITH_BUTTON =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_BUTTON"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP))
+ .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val SNAP_RESIZE_LEFT_WITH_DRAG =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_DRAG"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP))
+ .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val SNAP_RESIZE_RIGHT_WITH_DRAG =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_DRAG"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP))
.associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
@@ -181,5 +292,65 @@ class DesktopModeFlickerScenarios {
AppWindowReturnsToStartBoundsAndPosition(NON_RESIZABLE_APP)
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
+
+ val MAXIMIZE_APP =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("MAXIMIZE_APP"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP),
+ AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val MAXIMIZE_APP_NON_RESIZABLE =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("MAXIMIZE_APP_NON_RESIZABLE"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions =
+ AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ AppWindowMaintainsAspectRatioAlways(DESKTOP_MODE_APP),
+ AppWindowHasMaxBoundsInOnlyOneDimension(DESKTOP_MODE_APP)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val CASCADE_APP =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("CASCADE_APP"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection
+ ): Collection {
+ return transitions.filter { it.type == TransitionType.OPEN }
+ }
+ }
+ ),
+ assertions =
+ listOf(
+ AppWindowInsideDisplayBoundsAtEnd(DESKTOP_MODE_APP),
+ AppWindowOnTopAtEnd(DESKTOP_MODE_APP),
+ AppWindowBecomesVisible(DESKTOP_MODE_APP),
+ AppWindowAlignsWithOnlyOneDisplayCornerAtEnd(DESKTOP_MODE_APP)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2179566715542ad27791b10acde49e63d1192d14
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize app window by pressing the maximize button on the app header.
+ *
+ * Assert that the app window keeps the same increases in size, filling the vertical and horizontal
+ * stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppLandscape : MaximizeAppWindow(rotation = ROTATION_90) {
+ @ExpectedScenarios(["MAXIMIZE_APP"])
+ @Test
+ override fun maximizeAppWindow() = super.maximizeAppWindow()
+
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b173a60132b2d4d2da1046836942cf63fd4463ed
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP_NON_RESIZABLE
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize non-resizable app window by pressing the maximize button on the app header.
+ *
+ * Assert that the app window keeps the same increases in size, maintaining its aspect ratio, until
+ * filling the vertical or horizontal stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppNonResizableLandscape : MaximizeAppWindow(
+ rotation = ROTATION_90,
+ isResizable = false
+) {
+ @ExpectedScenarios(["MAXIMIZE_APP_NON_RESIZABLE"])
+ @Test
+ override fun maximizeAppWindow() = super.maximizeAppWindow()
+
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP_NON_RESIZABLE)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt
new file mode 100644
index 0000000000000000000000000000000000000000..88888eee83787aa26375995287d7154ca6b9ffe3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP_NON_RESIZABLE
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize non-resizable app window by pressing the maximize button on the app header.
+ *
+ * Assert that the app window keeps the same increases in size, maintaining its aspect ratio, until
+ * filling the vertical or horizontal stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppNonResizablePortrait : MaximizeAppWindow(isResizable = false) {
+ @ExpectedScenarios(["MAXIMIZE_APP_NON_RESIZABLE"])
+ @Test
+ override fun maximizeAppWindow() = super.maximizeAppWindow()
+
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP_NON_RESIZABLE)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b79fd203fe1e7e0990ed7605503c4004bd1ddc65
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize app window by pressing the maximize button on the app header.
+ *
+ * Assert that the app window keeps the same increases in size, filling the vertical and horizontal
+ * stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppPortrait : MaximizeAppWindow() {
+ @ExpectedScenarios(["MAXIMIZE_APP"])
+ @Test
+ override fun maximizeAppWindow() = super.maximizeAppWindow()
+
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a07fa99f655f7e108e6470401dfa377bcd9583ca
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppsInDesktopModeLandscape : OpenAppsInDesktopMode(rotation = ROTATION_90) {
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApps() = super.openApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c7a958aa7ce32e61ab3ae6c0ed1474ad226abad6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppsInDesktopModePortrait : OpenAppsInDesktopMode() {
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApps() = super.openApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0b98ba2a9cd4f258afb31c5df31bafb8d659dfcb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Resize app window using corner resize to the greatest possible height and width in
+ * landscape mode.
+ *
+ * Assert that the maximum window size constraint is maintained.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppToMaximumWindowSizeLandscape : ResizeAppWithCornerResize(
+ rotation = Rotation.ROTATION_90
+) {
+ @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"])
+ @Test
+ override fun resizeAppWithCornerResizeToMaximumSize() =
+ super.resizeAppWithCornerResizeToMaximumSize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MAXIMUM_SIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b1c04d38a46ca4d87363ed9a7a57609aef606f31
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Resize app window using corner resize to the greatest possible height and width in
+ * portrait mode.
+ *
+ * Assert that the maximum window size constraint is maintained.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppToMaximumWindowSizePortrait : ResizeAppWithCornerResize() {
+ @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"])
+ @Test
+ override fun resizeAppWithCornerResizeToMaximumSize() =
+ super.resizeAppWithCornerResizeToMaximumSize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MAXIMUM_SIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c3abf238dc0d3d1d09166b4f648cfb722d2813c2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeMouse : ResizeAppWithEdgeResize(InputMethod.MOUSE) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
new file mode 100644
index 0000000000000000000000000000000000000000..86b0e6f17b24b7e4c10cbc7c9c507a44dcf21966
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeStylus : ResizeAppWithEdgeResize(InputMethod.STYLUS) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e6bb9eff671580cb82bcb61b1816083f56d51aae
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeTouchpad : ResizeAppWithEdgeResize(InputMethod.TOUCHPAD) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b5090086f12981291d64e08ded5a0d4a6b53c494
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_BUTTON
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window using the Snap Left button from the maximize menu.
+ *
+ * Assert that the app window fills the left half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowLeftWithButton : SnapResizeAppWindowWithButton(toLeft = true) {
+ @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_BUTTON"])
+ @Test
+ override fun snapResizeAppWindowWithButton() = super.snapResizeAppWindowWithButton()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_BUTTON)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a22e7603bf0f116b1efd5fd5de27cef2039de495
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_DRAG
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window by dragging it to the left edge of the screen.
+ *
+ * Assert that the app window fills the left half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowLeftWithDrag : SnapResizeAppWindowWithDrag(toLeft = true) {
+ @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_DRAG"])
+ @Test
+ override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_DRAG)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt
new file mode 100644
index 0000000000000000000000000000000000000000..375a2b8a61ac930f19e75d350c4175630f934983
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_BUTTON
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window using the Snap Right button from the maximize menu.
+ *
+ * Assert that the app window fills the right half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowRightWithButton : SnapResizeAppWindowWithButton(toLeft = false) {
+ @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_BUTTON"])
+ @Test
+ override fun snapResizeAppWindowWithButton() = super.snapResizeAppWindowWithButton()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_BUTTON)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4a9daf7e2ea1d44fd8ccd691d2cc9fd29ba9c77e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_DRAG
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window by dragging it to the right edge of the screen.
+ *
+ * Assert that the app window fills the right half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowRightWithDrag : SnapResizeAppWindowWithDrag(toLeft = false) {
+ @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_DRAG"])
+ @Test
+ override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_DRAG)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a4dc52beb85d96f521867352cf28d2325fca6fe8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.CloseAllAppsWithAppHeaderExit
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [CloseAllAppsWithAppHeaderExit]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class CloseAllAppsWithAppHeaderExitTest() : CloseAllAppsWithAppHeaderExit()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3d95f97c09efe3f964b559a40014dd2bfd8d35c5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.EnterDesktopWithDrag
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [EnterDesktopWithDrag]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class EnterDesktopWithDragTest : EnterDesktopWithDrag()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..140c5ec1581213fb1f82671a3629c34c8b2e8706
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ExitDesktopWithDragToTopDragZone
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ExitDesktopWithDragToTopDragZone]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ExitDesktopWithDragToTopDragZoneTest : ExitDesktopWithDragToTopDragZone()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt
similarity index 62%
rename from packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt
index 7e09a108ea40c599b197a58e91b3758f21e70c86..3d3dcd09cc63f706499f3849d6aa240653c8c3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard
+package com.android.wm.shell.functional
-import com.android.systemui.Flags
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-/** Helper for reading or using the new picker UI flag. */
-@Suppress("NOTHING_TO_INLINE")
-object NewPickerUiKeyguardPreview {
-
- /** Is the new picker UI enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.newPickerUi()
-}
+/* Functional test for [MaximizeAppWindow]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class MaximizeAppWindowTest : MaximizeAppWindow()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..263e89f69e5a38b3bde0975b6a086d8cd8da92c2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [OpenAppsInDesktopMode]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class OpenAppsInDesktopModeTest : OpenAppsInDesktopMode()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..13f4775c10749af72fe5e04f7158521558754840
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppCornerMultiWindowAndPip
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppCornerMultiWindowAndPip]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppCornerMultiWindowAndPipTest : ResizeAppCornerMultiWindowAndPip()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bc9bb41bf320f99d9b8b639d41fb130fc2715231
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppCornerMultiWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppCornerMultiWindow]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppCornerMultiWindowTest : ResizeAppCornerMultiWindow()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..46168eb7c00258044613e966debaa2001c8c3d8a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppWithCornerResize]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppWithCornerResizeTest : ResizeAppWithCornerResize()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ee24200213399939b26f51d9750a5538985f06ed
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.server.wm.flicker.helpers.MotionEventHelper
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppWithEdgeResize]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppWithEdgeResizeTest :
+ ResizeAppWithEdgeResize(MotionEventHelper.InputMethod.TOUCHPAD)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..38e85c755481c1f9537406bcd56448e4a1c4e0b3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SnapResizeAppWindowWithButton]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SnapResizeAppWindowWithButtonTest : SnapResizeAppWindowWithButton()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..082a3fb0e1710d37754749842c4291d4e318eaaf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SnapResizeAppWindowWithDrag]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SnapResizeAppWindowWithDragTest : SnapResizeAppWindowWithDrag()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fdd0d8144130b9bba10abc01a6b3a9e4d9d2030e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SwitchToOverviewFromDesktop
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SwitchToOverviewFromDesktop]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SwitchToOverviewFromDesktopTest : SwitchToOverviewFromDesktop()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
index e9056f3c44d42c5306bf9ef28fa3b14fd6395ff0..351a700946540bd149091fae2e3dc509ec8ad039 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -33,15 +32,12 @@ import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class CloseAllAppsWithAppHeaderExit
-@JvmOverloads
+@Ignore("Base Test Class")
+abstract class CloseAllAppsWithAppHeaderExit
constructor(val rotation: Rotation = Rotation.ROTATION_0) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5a69b27d9fbbd9c5d885e9487ecbd3c48e2fc71c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import android.tools.traces.parsers.toFlickerComponent
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.Ignore
+
+/** Base test class for desktop CUJ with customizable test app. */
+@Ignore("Base Test Class")
+abstract class DesktopScenarioCustomAppTestBase(
+ isResizeable: Boolean = true,
+ isLandscapeApp: Boolean = true
+) {
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ val tapl = LauncherInstrumentation()
+ val wmHelper = WindowManagerStateHelper(instrumentation)
+ val device = UiDevice.getInstance(instrumentation)
+ // TODO(b/363181411): Consolidate in LetterboxAppHelper.
+ val testApp = when {
+ isResizeable && isLandscapeApp -> SimpleAppHelper(instrumentation)
+ isResizeable && !isLandscapeApp -> SimpleAppHelper(
+ instrumentation,
+ launcherName = ActivityOptions.PortraitOnlyActivity.LABEL,
+ component = ActivityOptions.PortraitOnlyActivity.COMPONENT.toFlickerComponent()
+ )
+ // NonResizeablAppHelper has no fixed orientation.
+ !isResizeable && isLandscapeApp -> NonResizeableAppHelper(instrumentation)
+ // Opens NonResizeablePortraitActivity.
+ else -> LetterboxAppHelper(instrumentation)
+ }.let { DesktopModeAppHelper(it) }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
index ca1dc1a7f658f36971f0895458e8c5670f42e3d9..3f9927f1fab6e939c1729ea3ae94eba6e7c6ee24 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.MailAppHelper
@@ -26,13 +25,11 @@ import com.android.window.flags.Flags
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
+@Ignore("Test Base Class")
+abstract class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
{
private val imeAppHelper = ImeAppHelper(instrumentation)
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
index 0f0d2df4337caf3d4105ecda8b3d18fbb01f323a..967bd29958c2892754595ff0234b3a718847c93b 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
@@ -16,37 +16,25 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
-import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
-import android.tools.traces.parsers.WindowManagerStateHelper
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.uiautomator.UiDevice
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class EnterDesktopWithDrag
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val tapl = LauncherInstrumentation()
- private val wmHelper = WindowManagerStateHelper(instrumentation)
- private val device = UiDevice.getInstance(instrumentation)
- private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+@Ignore("Test Base Class")
+abstract class EnterDesktopWithDrag
+constructor(
+ val rotation: Rotation = Rotation.ROTATION_0,
+ isResizeable: Boolean = true,
+ isLandscapeApp: Boolean = true,
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
@Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
@@ -55,6 +43,8 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ tapl.enableTransientTaskbar(false)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
index 533be8895fb989648147479e9dcf699eff999080..824c4482c1e6f466a6ca1540a12f2f4a7f03ef74 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
@@ -16,37 +16,24 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
-import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
-import android.tools.traces.parsers.WindowManagerStateHelper
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.uiautomator.UiDevice
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ExitDesktopWithDragToTopDragZone
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val tapl = LauncherInstrumentation()
- private val wmHelper = WindowManagerStateHelper(instrumentation)
- private val device = UiDevice.getInstance(instrumentation)
- private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+@Ignore("Test Base Class")
+abstract class ExitDesktopWithDragToTopDragZone
+constructor(
+ val rotation: Rotation = Rotation.ROTATION_0,
+ isResizeable: Boolean = true,
+ isLandscapeApp: Boolean = true,
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
@Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
index b812c596adbab40d4462bdf92a4613031b000441..426f40b5e81b821fc356e590487d11237938a6ab 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
@@ -16,10 +16,11 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.tools.NavBar
import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -36,11 +37,12 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
+
@RunWith(BlockJUnit4ClassRunner::class)
@Postsubmit
open class MaximizeAppWindow
@JvmOverloads
-constructor(rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) {
+constructor(private val rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -57,6 +59,9 @@ constructor(rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = tru
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
testApp.enterDesktopWithDrag(wmHelper, device)
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
new file mode 100644
index 0000000000000000000000000000000000000000..aad266fb83741795d78ed27d1290b7deded87c5f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract class OpenAppsInDesktopMode(val rotation: Rotation = Rotation.ROTATION_0) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val firstApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val secondApp = MailAppHelper(instrumentation)
+ private val thirdApp = NewTasksAppHelper(instrumentation)
+ private val fourthApp = ImeAppHelper(instrumentation)
+ private val fifthApp = NonResizeableAppHelper(instrumentation)
+
+ @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_3BUTTON, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ tapl.enableTransientTaskbar(false)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ firstApp.enterDesktopWithDrag(wmHelper, device)
+ }
+
+ @Test
+ open fun openApps() {
+ secondApp.launchViaIntent(wmHelper)
+ thirdApp.launchViaIntent(wmHelper)
+ fourthApp.launchViaIntent(wmHelper)
+ fifthApp.launchViaIntent(wmHelper)
+ }
+
+ @After
+ fun teardown() {
+ fifthApp.exit(wmHelper)
+ fourthApp.exit(wmHelper)
+ thirdApp.exit(wmHelper)
+ secondApp.exit(wmHelper)
+ firstApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
index b6bca7a94287f0fdc2609559514416bc1f3bc00f..bfee3181cbc0e232a4f9e9c9475ba875175c4a7d 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -34,15 +33,12 @@ import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppCornerMultiWindow
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class ResizeAppCornerMultiWindow
constructor(val rotation: Rotation = Rotation.ROTATION_0,
val horizontalChange: Int = 50,
val verticalChange: Int = -50) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
index 285ea13bb9d4a8d8b404f21db7d198cc223d8d96..5b1b64e7c562f4c9305460a0495800ea7d69edfd 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -35,15 +34,12 @@ import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppCornerMultiWindowAndPip
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class ResizeAppCornerMultiWindowAndPip
constructor(val rotation: Rotation = Rotation.ROTATION_0,
val horizontalChange: Int = 50,
val verticalChange: Int = -50) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
index 03d970fe4f39ba3db80610d3977b8ec4d0504091..bd25639466a3f91952dab08ea03f7785fb8e8d44 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -32,19 +31,15 @@ import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppWithCornerResize
-@JvmOverloads
-constructor(
+@Ignore("Test Base Class")
+abstract class ResizeAppWithCornerResize(
val rotation: Rotation = Rotation.ROTATION_0,
- val horizontalChange: Int = 50,
- val verticalChange: Int = -50,
+ val horizontalChange: Int = 200,
+ val verticalChange: Int = -200,
val appProperty: AppProperty = AppProperty.STANDARD
) {
@@ -83,6 +78,25 @@ constructor(
)
}
+ @Test
+ open fun resizeAppWithCornerResizeToMaximumSize() {
+ val maxResizeChange = 3000
+ testApp.cornerResize(
+ wmHelper,
+ device,
+ DesktopModeAppHelper.Corners.RIGHT_TOP,
+ maxResizeChange,
+ -maxResizeChange
+ )
+ testApp.cornerResize(
+ wmHelper,
+ device,
+ DesktopModeAppHelper.Corners.LEFT_BOTTOM,
+ -maxResizeChange,
+ maxResizeChange
+ )
+ }
+
@After
fun teardown() {
testApp.exit(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
new file mode 100644
index 0000000000000000000000000000000000000000..67802387b26768edae524876225fca0955a2e64b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.MotionEventHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract class ResizeAppWithEdgeResize
+constructor(
+ val inputMethod: MotionEventHelper.InputMethod,
+ val rotation: Rotation = Rotation.ROTATION_90
+) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val motionEventHelper = MotionEventHelper(instrumentation, inputMethod)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(
+ Flags.enableDesktopWindowingMode()
+ && Flags.enableWindowingEdgeDragResize() && tapl.isTablet
+ )
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ testApp.enterDesktopWithDrag(wmHelper, device)
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeRight() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.RIGHT
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeLeft() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.LEFT
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeTop() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.TOP
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeBottom() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.BOTTOM
+ )
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
index 685a3ba935d6949169102835947a6f0e90be6c69..2b40497844ef5e0812ebbbc494cee03d8d9a0d1c 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
@@ -17,7 +17,8 @@
package com.android.wm.shell.scenarios
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -26,18 +27,17 @@ import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SnapResizeAppWindowWithButton
-@JvmOverloads
-constructor(private val toLeft: Boolean = true, private val isResizable: Boolean = true) {
+@Ignore("Test Base Class")
+abstract class SnapResizeAppWindowWithButton
+constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -49,6 +49,10 @@ constructor(private val toLeft: Boolean = true, private val isResizable: Boolean
DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
}
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
index 8a4aa6343e4db9f400e79a27062e8e90e8eee591..b4bd7e1c52118a628e9ba74a6139b2b57da3dc9c 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
@@ -17,7 +17,8 @@
package com.android.wm.shell.scenarios
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -26,18 +27,17 @@ import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SnapResizeAppWindowWithDrag
-@JvmOverloads
-constructor(private val toLeft: Boolean = true, private val isResizable: Boolean = true) {
+@Ignore("Test Base Class")
+abstract class SnapResizeAppWindowWithDrag
+constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -49,6 +49,10 @@ constructor(private val toLeft: Boolean = true, private val isResizable: Boolean
DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
}
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
@@ -64,4 +68,4 @@ constructor(private val toLeft: Boolean = true, private val isResizable: Boolean
fun teardown() {
testApp.exit(wmHelper)
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
index 53e36e23fd953355b3ddc586cb97d3705c1fef92..dad2eb633c727e944b4a4ee9fd200d6ca4ba8654 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -31,20 +30,17 @@ import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
/**
* Base test for opening recent apps overview from desktop mode.
*
* Navigation mode can be passed as a constructor parameter, by default it is set to gesture navigation.
*/
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SwitchToOverviewFromDesktop
-@JvmOverloads
+@Ignore("Base Test Class")
+abstract class SwitchToOverviewFromDesktop
constructor(val navigationMode: NavBar = NavBar.MODE_GESTURAL) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
index dee67f3f2e0eb2ffb325b8e119af1a1b8354d334..c0fafef9677539cf26857cf518f5052a14be027a 100644
--- a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell
import android.app.Instrumentation
+import android.platform.test.rule.EnsureDeviceSettingsRule
import android.platform.test.rule.NavigationModeRule
import android.platform.test.rule.PressHomeRule
import android.platform.test.rule.UnlockScreenRule
@@ -49,5 +50,6 @@ object Utils {
)
)
.around(PressHomeRule())
+ .around(EnsureDeviceSettingsRule())
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 4abaf5bd4a387f460b2be12bc426960ec40d25b5..7305f4988aa94ba14b8c4e4f8dd6dc304cb9f426 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -68,6 +68,7 @@ java_defaults {
"flickerlib-helpers",
"flickerlib-trace_processor_shell",
"platform-test-annotations",
+ "platform-test-rules",
"wm-flicker-common-app-helpers",
"wm-flicker-common-assertions",
"launcher-helper-lib",
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..a36a4f85fa4e6d8af49eb5b574a615eaadb73c88
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS
@@ -0,0 +1,2 @@
+# Window Manager > App Compat
+# Bug component: 970984
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index b85d7936efc2cc6ad67ccbdc4ee67728303acffa..a9ed13a099f391e9ee4974fbf56e0c272c97948d 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,10 +16,14 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder
+import android.tools.flicker.subject.exceptions.IncorrectRegionException
+import android.tools.flicker.subject.layers.LayerSubject
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
@@ -29,6 +33,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
+import kotlin.math.abs
/**
* Test entering pip from an app via auto-enter property when navigating to home.
@@ -67,9 +72,24 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran
override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } }
- @FlakyTest(bugId = 293133362)
+ private val widthNotSmallerThan: LayerSubject.(LayerSubject) -> Unit = {
+ val width = visibleRegion.region.bounds.width()
+ val otherWidth = it.visibleRegion.region.bounds.width()
+ if (width < otherWidth && abs(width - otherWidth) > EPSILON) {
+ val errorMsgBuilder =
+ ExceptionMessageBuilder()
+ .forSubject(this)
+ .forIncorrectRegion("width. $width smaller than $otherWidth")
+ .setExpected(width)
+ .setActual(otherWidth)
+ throw IncorrectRegionException(errorMsgBuilder)
+ }
+ }
+
+ @Postsubmit
@Test
override fun pipLayerReduces() {
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
flicker.assertLayers {
val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
@@ -78,6 +98,32 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran
}
}
+ /** Checks that [pipApp] window's width is first decreasing then increasing. */
+ @Postsubmit
+ @Test
+ fun pipLayerWidthDecreasesThenIncreases() {
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+ var previousLayer = pipLayerList[0]
+ var currentLayer = previousLayer
+ var i = 0
+ invoke("layer area is decreasing") {
+ if (i < pipLayerList.size - 1) {
+ previousLayer = currentLayer
+ currentLayer = pipLayerList[++i]
+ previousLayer.widthNotSmallerThan(currentLayer)
+ }
+ }.then().invoke("layer are is increasing", true /* isOptional */) {
+ if (i < pipLayerList.size - 1) {
+ previousLayer = currentLayer
+ currentLayer = pipLayerList[++i]
+ currentLayer.widthNotSmallerThan(previousLayer)
+ }
+ }
+ }
+ }
+
/** Checks that [pipApp] window is animated towards default position in right bottom corner */
@FlakyTest(bugId = 255578530)
@Test
@@ -108,4 +154,9 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
+
+ companion object {
+ // TODO(b/363080056): A margin of error allowed on certain layer size calculations.
+ const val EPSILON = 1
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
index 70be58f06548dd9ca5ebad50df5f467e045e61d4..429774f890a58d4a9d7a48fd853fcf6f8c5df366 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -35,7 +35,7 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { pipApp.changeAspectRatio() }
+ transitions { pipApp.changeAspectRatio(wmHelper) }
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 90b9798c63292149a86b7b339943c1dda395cb9d..cbd4a528474ab08d1d101ef6867c9c98ad2a994b 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -181,6 +181,13 @@ class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
super.taskBarWindowIsAlwaysVisible()
}
+ // Overridden to remove @Postsubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun pipLayerHasCorrectCornersAtEnd() {
+ // No rounded corners as we go back to fullscreen in new orientation.
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index ed2a0a718c6c791c6c844c6105b16ced60f3bb62..578a9b53628964c9ffb0878d31bfe01ead0ff58c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -147,6 +147,12 @@ open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipT
@Test
override fun entireScreenCovered() = super.entireScreenCovered()
+ @Postsubmit
+ @Test
+ override fun pipLayerHasCorrectCornersAtEnd() {
+ flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) }
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
index 8cb81b46cf4d9c860d8f3ae8019946094fb6198d..f57335c2081b4ba6fc6f51e47664c5c0bb4a3959 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
@@ -70,6 +70,11 @@ abstract class ClosePipTransition(flicker: LegacyFlickerTest) : PipTransition(fl
}
}
+ @Test
+ override fun pipLayerHasCorrectCornersAtEnd() {
+ // PiP might have completely faded out by this point, so corner radii not applicable.
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
index 0742cf9c58871da34eb85f014f8c4f4e20bdc9b5..ce84eb644042df5d2faa3e1ed1ac04ae4eb263af 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.common
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -123,6 +124,12 @@ abstract class ExitPipToAppTransition(flicker: LegacyFlickerTest) : PipTransitio
}
}
+ @Postsubmit
+ @Test
+ override fun pipLayerHasCorrectCornersAtEnd() {
+ flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) }
+ }
+
/** {@inheritDoc} */
@Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
index 99c1ad2aaa4e7056bd9afe259a8601005d5d38c0..bc2bfdbe1df13ae0a87e2d32497b007f5815dcc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.pip.common
import android.app.Instrumentation
import android.content.Intent
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
import android.tools.flicker.legacy.FlickerBuilder
@@ -105,4 +106,10 @@ abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
.doesNotContain(false)
}
}
+
+ @Postsubmit
+ @Test
+ open fun pipLayerHasCorrectCornersAtEnd() {
+ flicker.assertLayersEnd { hasRoundedCorners(pipApp) }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 4d761e18b99079bf733ef75d1efeec794b4d1bc9..006a4a9ea4357b13ffdca44a8857d2d5f0d2dd2c 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -94,3 +94,10 @@ android_test {
"com.android.wm.shell.tests",
],
}
+
+test_module_config {
+ name: "WMShellUnitTests_shell_back",
+ base: "WMShellUnitTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.wm.shell.back"],
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
index 499870220190ef3a231a95798fd8621d4151c694..f31722d3c1a57194a38a8da943bc0f468f8c42b0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
@@ -19,6 +19,7 @@ package com.android.wm.shell;
import com.android.wm.shell.common.ShellExecutor;
import java.util.ArrayList;
+import java.util.List;
/**
* Really basic test executor. It just gathers all events in a blob. The only option is to
@@ -52,4 +53,9 @@ public class TestShellExecutor implements ShellExecutor {
mRunnables.remove(0).run();
}
}
+
+ /** Returns the list of callbacks for this executor. */
+ public List getCallbacks() {
+ return mRunnables;
+ }
}
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 859602ec709f8757a81322f03c05ce1ae082baa1..6fa37885b724f1f5f9a54bf5b79947df3875b715 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
@@ -50,8 +50,8 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.bubbles.BubbleData.TimeSource;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.google.common.collect.ImmutableList;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
index 50c4a18280267cf9f8aec9d3ed064563ce6a7882..dca5fc4c2fe007da0bc0f09cf2294bbfc11a5497 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
@@ -43,7 +43,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b14f1633e8fda842c15bacfe92c121fb795129f7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.pm.ActivityInfo
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+import android.graphics.Rect
+import android.os.Binder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import android.window.WindowContainerTransaction
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import kotlin.test.assertNotNull
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
+
+/**
+ * Test class for {@link DesktopActivityOrientationChangeHandler}
+ *
+ * Usage: atest WMShellUnitTests:DesktopActivityOrientationChangeHandlerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE)
+class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ @Mock lateinit var testExecutor: ShellExecutor
+ @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
+ @Mock lateinit var transitions: Transitions
+ @Mock lateinit var resizeTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
+ @Mock lateinit var taskStackListener: TaskStackListenerImpl
+
+ private lateinit var mockitoSession: StaticMockitoSession
+ private lateinit var handler: DesktopActivityOrientationChangeHandler
+ private lateinit var shellInit: ShellInit
+ private lateinit var taskRepository: DesktopModeTaskRepository
+ // Mock running tasks are registered here so we can get the list from mock shell task organizer.
+ private val runningTasks = mutableListOf()
+
+ @Before
+ fun setUp() {
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+ shellInit = spy(ShellInit(testExecutor))
+ taskRepository = DesktopModeTaskRepository()
+ whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+ whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+
+ handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
+ taskStackListener, resizeTransitionHandler, taskRepository)
+
+ shellInit.init()
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+
+ runningTasks.clear()
+ }
+
+ @Test
+ fun instantiate_addInitCallback() {
+ verify(shellInit).addInitCallback(any(), any())
+ }
+
+ @Test
+ fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
+ clearInvocations(shellInit)
+
+ handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
+ taskStackListener, resizeTransitionHandler, taskRepository)
+
+ verify(shellInit, never()).addInitCallback(any(),
+ any())
+ }
+
+ @Test
+ fun handleActivityOrientationChange_resizeable_doNothing() {
+ val task = setUpFreeformTask()
+
+ taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
+ SCREEN_ORIENTATION_LANDSCAPE)
+
+ verify(resizeTransitionHandler, never()).startTransition(any(), any())
+ }
+
+ @Test
+ fun handleActivityOrientationChange_nonResizeableFullscreen_doNothing() {
+ val task = createFullscreenTask()
+ task.isResizeable = false
+ val activityInfo = ActivityInfo()
+ activityInfo.screenOrientation = SCREEN_ORIENTATION_PORTRAIT
+ task.topActivityInfo = activityInfo
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ taskRepository.addActiveTask(DEFAULT_DISPLAY, task.taskId)
+ taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task.taskId, visible = true)
+ runningTasks.add(task)
+
+ taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
+ SCREEN_ORIENTATION_LANDSCAPE)
+
+ verify(resizeTransitionHandler, never()).startTransition(any(), any())
+ }
+
+ @Test
+ fun handleActivityOrientationChange_nonResizeablePortrait_requestSameOrientation_doNothing() {
+ val task = setUpFreeformTask(isResizeable = false)
+ val newTask = setUpFreeformTask(isResizeable = false,
+ orientation = SCREEN_ORIENTATION_SENSOR_PORTRAIT)
+
+ handler.handleActivityOrientationChange(task, newTask)
+
+ verify(resizeTransitionHandler, never()).startTransition(any(), any())
+ }
+
+ @Test
+ fun handleActivityOrientationChange_notInDesktopMode_doNothing() {
+ val task = setUpFreeformTask(isResizeable = false)
+ taskRepository.updateTaskVisibility(task.displayId, task.taskId, visible = false)
+
+ taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
+ SCREEN_ORIENTATION_LANDSCAPE)
+
+ verify(resizeTransitionHandler, never()).startTransition(any(), any())
+ }
+
+ @Test
+ fun handleActivityOrientationChange_nonResizeablePortrait_respectLandscapeRequest() {
+ val task = setUpFreeformTask(isResizeable = false)
+ val oldBounds = task.configuration.windowConfiguration.bounds
+ val newTask = setUpFreeformTask(isResizeable = false,
+ orientation = SCREEN_ORIENTATION_LANDSCAPE)
+
+ handler.handleActivityOrientationChange(task, newTask)
+
+ val wct = getLatestResizeDesktopTaskWct()
+ val finalBounds = findBoundsChange(wct, newTask)
+ assertNotNull(finalBounds)
+ val finalWidth = finalBounds.width()
+ val finalHeight = finalBounds.height()
+ // Bounds is landscape.
+ assertTrue(finalWidth > finalHeight)
+ // Aspect ratio remains the same.
+ assertEquals(oldBounds.height() / oldBounds.width(), finalWidth / finalHeight)
+ // Anchor point for resizing is at the center.
+ assertEquals(oldBounds.centerX(), finalBounds.centerX())
+ }
+
+ @Test
+ fun handleActivityOrientationChange_nonResizeableLandscape_respectPortraitRequest() {
+ val oldBounds = Rect(0, 0, 500, 200)
+ val task = setUpFreeformTask(
+ isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE, bounds = oldBounds
+ )
+ val newTask = setUpFreeformTask(isResizeable = false, bounds = oldBounds)
+
+ handler.handleActivityOrientationChange(task, newTask)
+
+ val wct = getLatestResizeDesktopTaskWct()
+ val finalBounds = findBoundsChange(wct, newTask)
+ assertNotNull(finalBounds)
+ val finalWidth = finalBounds.width()
+ val finalHeight = finalBounds.height()
+ // Bounds is portrait.
+ assertTrue(finalHeight > finalWidth)
+ // Aspect ratio remains the same.
+ assertEquals(oldBounds.width() / oldBounds.height(), finalHeight / finalWidth)
+ // Anchor point for resizing is at the center.
+ assertEquals(oldBounds.centerX(), finalBounds.centerX())
+ }
+
+ private fun setUpFreeformTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ isResizeable: Boolean = true,
+ orientation: Int = SCREEN_ORIENTATION_PORTRAIT,
+ bounds: Rect? = Rect(0, 0, 200, 500)
+ ): RunningTaskInfo {
+ val task = createFreeformTask(displayId, bounds)
+ val activityInfo = ActivityInfo()
+ activityInfo.screenOrientation = orientation
+ task.topActivityInfo = activityInfo
+ task.isResizeable = isResizeable
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ taskRepository.addActiveTask(displayId, task.taskId)
+ taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
+ taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
+ runningTasks.add(task)
+ return task
+ }
+
+ private fun getLatestResizeDesktopTaskWct(
+ currentBounds: Rect? = null
+ ): WindowContainerTransaction {
+ val arg: ArgumentCaptor =
+ ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(resizeTransitionHandler, atLeastOnce())
+ .startTransition(capture(arg), eq(currentBounds))
+ return arg.value
+ }
+
+ private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
+ wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index e49eb36fc04a089e1c84acb378d573781b23bf90..d399b20abb2a6f2240d81ad59ae19251906de3e0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -22,6 +22,8 @@ import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.os.IBinder
+import android.os.SystemProperties
+import android.os.Trace
import android.testing.AndroidTestingRunner
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
@@ -38,6 +40,7 @@ import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
@@ -86,7 +89,11 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
@JvmField
@Rule
val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!!
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(DesktopModeStatus::class.java)
+ .mockStatic(SystemProperties::class.java)
+ .mockStatic(Trace::class.java)
+ .build()!!
private val testExecutor = mock()
private val mockShellInit = mock()
@@ -695,6 +702,17 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
assertNotNull(sessionId)
verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason))
verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate))
+ ExtendedMockito.verify {
+ Trace.setCounter(
+ eq(Trace.TRACE_TAG_WINDOW_MANAGER),
+ eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_NAME),
+ eq(taskUpdate.visibleTaskCount.toLong()))
+ }
+ ExtendedMockito.verify {
+ SystemProperties.set(
+ eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
+ eq(taskUpdate.visibleTaskCount.toString()))
+ }
verifyZeroInteractions(desktopModeEventLogger)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8fab410adc303892ec889b1c9667a62992e25958
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeUtilsTest {
+ @Test
+ fun isTaskBoundsEqual_stableBoundsAreEqual_returnTrue() {
+ assertThat(isTaskBoundsEqual(task2Bounds, stableBounds)).isTrue()
+ }
+
+ @Test
+ fun isTaskBoundsEqual_stableBoundsAreNotEqual_returnFalse() {
+ assertThat(isTaskBoundsEqual(task4Bounds, stableBounds)).isFalse()
+ }
+
+ @Test
+ fun isTaskWidthOrHeightEqual_stableBoundsAreEqual_returnTrue() {
+ assertThat(isTaskWidthOrHeightEqual(task2Bounds, stableBounds)).isTrue()
+ }
+
+ @Test
+ fun isTaskWidthOrHeightEqual_stableBoundWidthIsEquals_returnTrue() {
+ assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue()
+ }
+
+ @Test
+ fun isTaskWidthOrHeightEqual_stableBoundHeightIsEquals_returnTrue() {
+ assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue()
+ }
+
+ @Test
+ fun isTaskWidthOrHeightEqual_stableBoundsWidthOrHeightAreNotEquals_returnFalse() {
+ assertThat(isTaskWidthOrHeightEqual(task1Bounds, stableBounds)).isTrue()
+ }
+
+ private companion object {
+ val task1Bounds = Rect(0, 0, 0, 0)
+ val task2Bounds = Rect(1, 1, 1, 1)
+ val task3Bounds = Rect(0, 1, 0, 1)
+ val task4Bounds = Rect(1, 2, 2, 1)
+ val stableBounds = Rect(1, 1, 1, 1)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index d2487209dcefc6b8bb6858c77e01fc39256589ed..10557dd9b43958fae56eb5f66faaafc5f755f14c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -84,6 +84,7 @@ import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
+import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
@@ -122,12 +123,12 @@ import org.mockito.ArgumentMatchers.isA
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.eq
@@ -175,6 +176,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock
private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockSurface: SurfaceControl
+ @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -237,6 +239,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
recentsTransitionStateListener = captor.value
+
+ controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
}
private fun createController(): DesktopTasksController {
@@ -281,6 +285,52 @@ class DesktopTasksControllerTest : ShellTestCase() {
verify(shellInit).addInitCallback(any(), any())
}
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() {
+ setUpFreeformTask()
+
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() {
+ val task1 = setUpFreeformTask()
+
+ val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+ controller.toggleDesktopTaskSize(task1)
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+
+ assertThat(argumentCaptor.value).isTrue()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ setUpFreeformTask(bounds = stableBounds, active = true)
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFullScreenTask_returnFalse() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ val task1 = setUpFreeformTask(bounds = stableBounds, active = true)
+
+ val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+ controller.toggleDesktopTaskSize(task1)
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+
+ assertThat(argumentCaptor.value).isFalse()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom))
+
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+ }
+
+
@Test
fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
@@ -2809,7 +2859,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun getSnapBounds_calculatesBoundsForResizable() {
+ fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
val bounds = Rect(100, 100, 300, 300)
val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
topActivityInfo = ActivityInfo().apply {
@@ -2824,12 +2874,44 @@ class DesktopTasksControllerTest : ShellTestCase() {
STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
)
- controller.snapToHalfScreen(task, currentDragBounds, SnapPosition.LEFT)
+ controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)
// Assert bounds set to stable bounds
val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
}
+ @Test
+ fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ // Set up task to already be in snapped-left bounds
+ val bounds = Rect(
+ STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
+ )
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+ topActivityInfo = ActivityInfo().apply {
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+ configuration.windowConfiguration.appBounds = bounds
+ }
+ isResizeable = true
+ }
+
+ // Attempt to snap left again
+ val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
+ controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)
+
+ // Assert that task is NOT updated via WCT
+ verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+
+ // Assert that task leash is updated via Surface Animations
+ verify(mReturnToDragStartAnimator).start(
+ eq(task.taskId),
+ eq(mockSurface),
+ eq(currentDragBounds),
+ eq(bounds),
+ eq(true)
+ )
+ }
+
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun handleSnapResizingTask_nonResizable_snapsToHalfScreen() {
@@ -2861,7 +2943,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
eq(task.taskId),
eq(mockSurface),
eq(currentDragBounds),
- eq(preDragBounds)
+ eq(preDragBounds),
+ eq(false)
)
}
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 a8d40db096dde4fd5f3e8406f08908f3727f10f7..386253c19c8290e14fbcfca4c447e8f70a88d733 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
@@ -46,6 +46,7 @@ import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -136,6 +137,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
mMainExecutor = new TestShellExecutor();
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
+ when(mContext.getSystemService(KeyguardManager.class))
+ .thenReturn(mock(KeyguardManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
mDisplayInsetsController, mMainExecutor));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
index 27e0b196f0be607fb9ec00a52bfc8861d4ca6a4e..b9bf95b16e7070320c5d7ad81d8a63c3dbc4a522 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.DEFAULT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.DEFAULT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
similarity index 97%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
index 5b22eddcb6ee599a83a18ed75c021cfdb57d99d7..641063c2707670ca291268d20f81275aa7c3545e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.os.Parcel
import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE
@@ -41,6 +41,7 @@ class BubbleInfoTest : ShellTestCase() {
"com.some.package",
"title",
"Some app",
+ true,
true
)
val parcel = Parcel.obtain()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt
similarity index 75%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt
index 2a6754cbdd1af69d54ad2d4513d40fd29ea7ffe0..d3e291f7dd1f5426bea77ec72c589b05bde43225 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.shared.navigationbar
+package com.android.wm.shell.shared.handles
+
import android.graphics.Rect
import android.testing.TestableLooper.RunWithLooper
@@ -24,16 +25,15 @@ import android.view.ViewRootImpl
import androidx.concurrent.futures.DirectExecutor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.time.FakeSystemClock
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
@@ -42,11 +42,12 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.argumentCaptor
@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
-class RegionSamplingHelperTest : SysuiTestCase() {
+class RegionSamplingHelperTest : ShellTestCase() {
@Mock
lateinit var sampledView: View
@@ -72,12 +73,16 @@ class RegionSamplingHelperTest : SysuiTestCase() {
whenever(surfaceControl.isValid).thenReturn(true)
whenever(wrappedSurfaceControl.isValid).thenReturn(true)
whenever(samplingCallback.isSamplingEnabled).thenReturn(true)
- regionSamplingHelper = object : RegionSamplingHelper(sampledView, samplingCallback,
- DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener) {
- override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
- return wrappedSurfaceControl
+ getInstrumentation().runOnMainSync(Runnable {
+ regionSamplingHelper = object : RegionSamplingHelper(
+ sampledView, samplingCallback,
+ DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener
+ ) {
+ override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
+ return wrappedSurfaceControl
+ }
}
- }
+ })
regionSamplingHelper.setWindowVisible(true)
}
@@ -99,7 +104,7 @@ class RegionSamplingHelperTest : SysuiTestCase() {
regionSamplingHelper.setWindowHasBlurs(true)
regionSamplingHelper.start(Rect(0, 0, 100, 100))
verify(compositionListener, never())
- .register(any(), anyInt(), eq(wrappedSurfaceControl), any())
+ .register(any(), anyInt(), eq(wrappedSurfaceControl), any())
}
@Test
@@ -112,35 +117,38 @@ class RegionSamplingHelperTest : SysuiTestCase() {
@Test
fun testCompositionSamplingListener_has_nonEmptyRect() {
// simulate race condition
- val fakeExecutor = FakeExecutor(FakeSystemClock()) // pass in as backgroundExecutor
+ val fakeExecutor = TestShellExecutor() // pass in as backgroundExecutor
val fakeSamplingCallback = mock(RegionSamplingHelper.SamplingCallback::class.java)
whenever(fakeSamplingCallback.isSamplingEnabled).thenReturn(true)
whenever(wrappedSurfaceControl.isValid).thenReturn(true)
-
- regionSamplingHelper = object : RegionSamplingHelper(sampledView, fakeSamplingCallback,
- DirectExecutor.INSTANCE, fakeExecutor, compositionListener) {
- override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
- return wrappedSurfaceControl
+ getInstrumentation().runOnMainSync(Runnable {
+ regionSamplingHelper = object : RegionSamplingHelper(
+ sampledView, fakeSamplingCallback,
+ DirectExecutor.INSTANCE, fakeExecutor, compositionListener
+ ) {
+ override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
+ return wrappedSurfaceControl
+ }
}
- }
+ })
regionSamplingHelper.setWindowVisible(true)
regionSamplingHelper.start(Rect(0, 0, 100, 100))
// make sure background task is enqueued
- assertThat(fakeExecutor.numPending()).isEqualTo(1)
+ assertThat(fakeExecutor.getCallbacks().size).isEqualTo(1)
// make sure regionSamplingHelper will have empty Rect
whenever(fakeSamplingCallback.getSampledRegion(any())).thenReturn(Rect(0, 0, 0, 0))
regionSamplingHelper.onLayoutChange(sampledView, 0, 0, 0, 0, 0, 0, 0, 0)
// resume running of background thread
- fakeExecutor.runAllReady()
+ fakeExecutor.flushAll()
// grab Rect passed into compositionSamplingListener and make sure it's not empty
val argumentGrabber = argumentCaptor()
verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl),
- argumentGrabber.capture())
- assertThat(argumentGrabber.value.isEmpty).isFalse()
+ argumentGrabber.capture())
+ assertThat(argumentGrabber.firstValue.isEmpty).isFalse()
}
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
deleted file mode 100644
index b1befc46f383bc3f5cf0fe30eae0e37c1c7eae55..0000000000000000000000000000000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/** Tests for {@link MainStage} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MainStageTests extends ShellTestCase {
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
- @Mock private SurfaceControl mRootLeash;
- @Mock private IconProvider mIconProvider;
- private WindowContainerTransaction mWct = new WindowContainerTransaction();
- private SurfaceSession mSurfaceSession = new SurfaceSession();
- private MainStage mMainStage;
-
- @Before
- @UiThreadTest
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
- mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty());
- mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
- }
-
- @Test
- public void testActiveDeactivate() {
- mMainStage.activate(mWct, true /* reparent */);
- assertThat(mMainStage.isActive()).isTrue();
-
- mMainStage.deactivate(mWct);
- assertThat(mMainStage.isActive()).isFalse();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
deleted file mode 100644
index 549bd3fcabfb9e17ae93021d89d3e67067f4db7d..0000000000000000000000000000000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-
-import java.util.Optional;
-
-/** Tests for {@link SideStage} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SideStageTests extends ShellTestCase {
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private ActivityManager.RunningTaskInfo mRootTask;
- @Mock private SurfaceControl mRootLeash;
- @Mock private IconProvider mIconProvider;
- @Spy private WindowContainerTransaction mWct;
- private SurfaceSession mSurfaceSession = new SurfaceSession();
- private SideStage mSideStage;
-
- @Before
- @UiThreadTest
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mRootTask = new TestRunningTaskInfoBuilder().build();
- mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty());
- mSideStage.onTaskAppeared(mRootTask, mRootLeash);
- }
-
- @Test
- public void testAddTask() {
- final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
-
- mSideStage.addTask(task, mWct);
-
- verify(mWct).reparent(eq(task.token), eq(mRootTask.token), eq(true));
- }
-
- @Test
- public void testRemoveTask() {
- final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
- assertThat(mSideStage.removeTask(task.taskId, null, mWct)).isFalse();
-
- mSideStage.mChildrenTaskInfo.put(task.taskId, task);
- assertThat(mSideStage.removeTask(task.taskId, null, mWct)).isTrue();
- verify(mWct).reparent(eq(task.token), isNull(), eq(false));
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index aa96c45489dd68aedc38f7bda0e583f4f99279dc..751275b9e167d3fa509772f57f1eec154c604a23 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -74,10 +74,10 @@ public class SplitTestUtils {
final SurfaceControl mRootLeash;
TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage,
- DisplayController displayController, DisplayImeController imeController,
- DisplayInsetsController insetsController, SplitLayout splitLayout,
- Transitions transitions, TransactionPool transactionPool,
+ ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
+ StageTaskListener sideStage, DisplayController displayController,
+ DisplayImeController imeController, DisplayInsetsController insetsController,
+ SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
ShellExecutor mainExecutor, Handler mainHandler,
Optional recentTasks,
LaunchAdjacentController launchAdjacentController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index abe3dcc1eb80d49e8850f4e1cf9c78f18acc82bf..af288c81616d46b0aa661f49b374baa3e63cbd08 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -116,8 +116,8 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private SplitScreen.SplitInvocationListener mInvocationListener;
private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
private SplitLayout mSplitLayout;
- private MainStage mMainStage;
- private SideStage mSideStage;
+ private StageTaskListener mMainStage;
+ private StageTaskListener mSideStage;
private StageCoordinator mStageCoordinator;
private SplitScreenTransitions mSplitScreenTransitions;
@@ -133,11 +133,11 @@ public class SplitTransitionTests extends ShellTestCase {
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
- mMainStage = spy(new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
mIconProvider, Optional.of(mWindowDecorViewModel)));
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
- mSideStage = spy(new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
mIconProvider, Optional.of(mWindowDecorViewModel)));
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
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 0054cb6ccc8c2e64542bf17b62bfd43615d6c4e9..ce343b8a7fa9fcaff953ccadb87cbda0552b6fe5 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
@@ -97,9 +97,9 @@ public class StageCoordinatorTests extends ShellTestCase {
@Mock
private SyncTransactionQueue mSyncQueue;
@Mock
- private MainStage mMainStage;
+ private StageTaskListener mMainStage;
@Mock
- private SideStage mSideStage;
+ private StageTaskListener mSideStage;
@Mock
private SplitLayout mSplitLayout;
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 946a7ef7d8c3a50b3c8cd413ecbcd1d72e8891ed..8b5cb97505c0db2ce94ec4d572932fa978416c60 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -52,6 +53,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.util.Optional;
@@ -76,6 +78,8 @@ public final class StageTaskListenerTests extends ShellTestCase {
private IconProvider mIconProvider;
@Mock
private WindowDecorViewModel mWindowDecorViewModel;
+ @Spy
+ private WindowContainerTransaction mWct;
@Captor
private ArgumentCaptor mRunnableCaptor;
private SurfaceSession mSurfaceSession = new SurfaceSession();
@@ -177,4 +181,31 @@ public final class StageTaskListenerTests extends ShellTestCase {
mStageTaskListener.evictAllChildren(wct);
assertFalse(wct.isEmpty());
}
+
+ @Test
+ public void testAddTask() {
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+ mStageTaskListener.addTask(task, mWct);
+
+ verify(mWct).reparent(eq(task.token), eq(mRootTask.token), eq(true));
+ }
+
+ @Test
+ public void testRemoveTask() {
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+ assertThat(mStageTaskListener.removeTask(task.taskId, null, mWct)).isFalse();
+
+ mStageTaskListener.mChildrenTaskInfo.put(task.taskId, task);
+ assertThat(mStageTaskListener.removeTask(task.taskId, null, mWct)).isTrue();
+ verify(mWct).reparent(eq(task.token), isNull(), eq(false));
+ }
+
+ @Test
+ public void testActiveDeactivate() {
+ mStageTaskListener.activate(mWct, true /* reparent */);
+ assertThat(mStageTaskListener.isActive()).isTrue();
+
+ mStageTaskListener.deactivate(mWct);
+ assertThat(mStageTaskListener.isActive()).isFalse();
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 0434742c571b6595a8c5d99959828a6ae8f8109a..198488582700ffeb021b1b00674288b9a31ff7bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -293,16 +293,6 @@ public class TaskViewTest extends ShellTestCase {
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
}
- @Test
- public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() {
- assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
- verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
-
- mTaskViewTaskController.onTaskVanished(mTaskInfo);
- verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
- }
-
@Test
public void testOnNewTask_noSurface() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
@@ -442,19 +432,6 @@ public class TaskViewTest extends ShellTestCase {
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
}
- @Test
- public void testUnsetOnBackPressedOnTaskRoot() {
- assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
- WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
- new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
- mLeash, wct);
- verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
-
- mTaskViewTaskController.prepareCloseAnimation();
- verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
- }
-
@Test
public void testSetObscuredTouchRect() {
mTaskView.setObscuredTouchRect(
@@ -713,4 +690,26 @@ public class TaskViewTest extends ShellTestCase {
verify(mViewHandler).post(any());
verify(mTaskView).setResizeBackgroundColor(eq(Color.BLUE));
}
+
+ @Test
+ public void testOnAppeared_setsTrimmableTask() {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+
+ assertThat(wct.getHierarchyOps().get(0).isTrimmableFromRecents()).isFalse();
+ }
+
+ @Test
+ public void testMoveToFullscreen_callsTaskRemovalStarted() {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskViewTaskController.moveToFullscreen();
+
+ verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index d2adae181f7b280b136615fae7272c001041dd8e..f51a9608d4426fc2d5b5e1cbb62b4620461350a9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -38,6 +38,9 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
@@ -46,6 +49,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -57,6 +61,7 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -70,6 +75,8 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class HomeTransitionObserverTest extends ShellTestCase {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
private final Context mContext =
@@ -187,6 +194,7 @@ public class HomeTransitionObserverTest extends ShellTestCase {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_MIGRATE_PREDICTIVE_BACK_TRANSITION)
public void testHomeActivityWithBackGestureNotifiesHomeIsVisible() throws RemoteException {
TransitionInfo info = mock(TransitionInfo.class);
TransitionInfo.Change change = mock(TransitionInfo.Change.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index da0aca7b3b0f72cc8cac3d6bccae8069fb49728b..3dd8a2bacbcd38997b139ccb11743fcc53ef15db 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -34,6 +34,7 @@ import android.hardware.display.VirtualDisplay
import android.hardware.input.InputManager
import android.net.Uri
import android.os.Handler
+import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.CheckFlagsRule
@@ -72,6 +73,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser
+import com.android.wm.shell.apptoweb.AssistContentRequester
import com.android.wm.shell.common.DisplayChangeController
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
@@ -79,6 +81,7 @@ import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.DesktopTasksLimiter
@@ -111,7 +114,7 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argThat
@@ -162,11 +165,15 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Mock private lateinit var mockWindowManager: IWindowManager
@Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser
+ @Mock private lateinit var mockUserHandle: UserHandle
+ @Mock private lateinit var mockAssistContentRequester: AssistContentRequester
@Mock private lateinit var mockToast: Toast
private val bgExecutor = TestShellExecutor()
@Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper
@Mock private lateinit var mockTasksLimiter: DesktopTasksLimiter
@Mock private lateinit var mockFreeformTaskTransitionStarter: FreeformTaskTransitionStarter
+ @Mock private lateinit var mockActivityOrientationChangeHandler:
+ DesktopActivityOrientationChangeHandler
private lateinit var spyContext: TestableContext
private val transactionFactory = Supplier {
@@ -213,6 +220,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
mockTransitions,
Optional.of(mockDesktopTasksController),
mockGenericLinksParser,
+ mockAssistContentRequester,
mockMultiInstanceHelper,
mockDesktopModeWindowDecorFactory,
mockInputMonitorFactory,
@@ -220,7 +228,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
mockRootTaskDisplayAreaOrganizer,
windowDecorByTaskIdSpy,
mockInteractionJankMonitor,
- Optional.of(mockTasksLimiter)
+ Optional.of(mockTasksLimiter),
+ Optional.of(mockActivityOrientationChangeHandler)
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -594,6 +603,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Test
fun testOnDecorSnappedLeft_snapResizes() {
+ val taskSurfaceCaptor = argumentCaptor()
val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor>
val decor = createOpenTaskDecoration(
@@ -604,8 +614,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onLeftSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.LEFT)
+ )
+ assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface)
}
@Test
@@ -626,6 +641,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun testOnSnapResizeLeft_nonResizable_decorSnappedLeft() {
+ val taskSurfaceCaptor = argumentCaptor()
val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor>
val decor = createOpenTaskDecoration(
@@ -636,8 +652,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onLeftSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.LEFT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -654,12 +675,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onLeftSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT))
verify(mockToast).show()
}
@Test
fun testOnDecorSnappedRight_snapResizes() {
+ val taskSurfaceCaptor = argumentCaptor()
val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor>
val decor = createOpenTaskDecoration(
@@ -670,8 +692,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onRightSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.RIGHT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -692,6 +719,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun testOnSnapResizeRight_nonResizable_decorSnappedRight() {
+ val taskSurfaceCaptor = argumentCaptor()
val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor>
val decor = createOpenTaskDecoration(
@@ -702,8 +730,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onRightSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.RIGHT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -720,7 +753,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onRightSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT))
verify(mockToast).show()
}
@@ -888,12 +921,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
openInBrowserListenerCaptor.value.accept(uri)
- verify(spyContext).startActivity(argThat { intent ->
+ verify(spyContext).startActivityAsUser(argThat { intent ->
intent.data == uri
&& ((intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != 0)
&& intent.categories.contains(Intent.CATEGORY_LAUNCHER)
&& intent.action == Intent.ACTION_MAIN
- })
+ }, eq(mockUserHandle))
}
@Test
@@ -1027,6 +1060,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
+ taskSurface: SurfaceControl = SurfaceControl(),
onMaxOrRestoreListenerCaptor: ArgumentCaptor> =
forClass(Function0::class.java) as ArgumentCaptor>,
onLeftSnapClickListenerCaptor: ArgumentCaptor> =
@@ -1045,7 +1079,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
forClass(View.OnClickListener::class.java) as ArgumentCaptor
): DesktopModeWindowDecoration {
val decor = setUpMockDecorationForTask(createTask(windowingMode = windowingMode))
- onTaskOpening(decor.mTaskInfo)
+ onTaskOpening(decor.mTaskInfo, taskSurface)
verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())
verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())
verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())
@@ -1100,10 +1134,11 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
whenever(
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
- any(), any(), any())
+ any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.isFocused).thenReturn(task.isFocused)
+ whenever(decoration.user).thenReturn(mockUserHandle)
if (task.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
whenever(mockSplitScreenController.isTaskInSplitScreen(task.taskId))
.thenReturn(true)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 596adfb98a705314532e3f54875496743ae22c0f..b9e542a0e0c16391d218166763bd2a7bba4a0fc6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -46,6 +46,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.assist.AssistContent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -88,6 +89,7 @@ import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
@@ -133,6 +135,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private static final Uri TEST_URI1 = Uri.parse("https://www.google.com/");
private static final Uri TEST_URI2 = Uri.parse("https://docs.google.com/");
+ private static final Uri TEST_URI3 = Uri.parse("https://slides.google.com/");
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -173,9 +176,14 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private AppToWebGenericLinksParser mMockGenericLinksParser;
@Mock
+ private WindowManager mMockWindowManager;
+ @Mock
+ private AssistContentRequester mMockAssistContentRequester;
+ @Mock
private HandleMenu mMockHandleMenu;
@Mock
private HandleMenuFactory mMockHandleMenuFactory;
+ @Mock
private MultiInstanceHelper mMockMultiInstanceHelper;
@Captor
private ArgumentCaptor> mOnMaxMenuHoverChangeListener;
@@ -186,7 +194,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private SurfaceControl.Transaction mMockTransaction;
private StaticMockitoSession mMockitoSession;
private TestableContext mTestableContext;
- private ShellExecutor mBgExecutor = new TestShellExecutor();
+ private final ShellExecutor mBgExecutor = new TestShellExecutor();
+ private final AssistContent mAssistContent = new AssistContent();
/** Set up run before test class. */
@BeforeClass
@@ -220,9 +229,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final Display defaultDisplay = mock(Display.class);
doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
- when(mMockHandleMenuFactory.create(any(), anyInt(), any(), any(),
+ when(mMockHandleMenuFactory.create(any(), any(), anyInt(), any(), any(),
any(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), anyInt()))
.thenReturn(mMockHandleMenu);
+ when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
}
@After
@@ -385,6 +395,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void updateRelayoutParams_fullscreen_inputChannelNotNeeded() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -401,6 +412,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void updateRelayoutParams_multiwindow_inputChannelNotNeeded() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -491,7 +503,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
.isTrue();
}
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
+ @Test
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -518,7 +530,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -530,7 +541,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_fullscreenTask_postsViewHostCreation() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -559,7 +569,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_removesExistingHandlerCallback() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -574,7 +583,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void close_removesExistingHandlerCallback() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -671,10 +679,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
public void capturedLink_handleMenuBrowserLinkSetToCapturedLinkIfValid() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
+ TEST_URI3 /* generic link */);
// Verify handle menu's browser link set as captured link
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI1);
}
@@ -683,7 +692,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
public void capturedLink_postsOnCapturedLinkExpiredRunnable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Run runnable to set captured link to expired
@@ -692,7 +702,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
// Verify captured link is no longer valid by verifying link is not set as handle menu
// browser link.
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -701,7 +711,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
public void capturedLink_capturedLinkNotResetToSameLink() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Run runnable to set captured link to expired
@@ -712,7 +723,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
decor.relayout(taskInfo);
// Verify handle menu's browser link not set to captured link since link is expired
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -721,11 +732,12 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
public void capturedLink_capturedLinkStillUsedIfExpiredAfterHandleMenuCreation() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Create handle menu before link expires
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
// Run runnable to set captured link to expired
verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
@@ -733,7 +745,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
// Verify handle menu's browser link is set to captured link since menu was opened before
// captured link expired
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI1);
}
@@ -742,12 +754,13 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
public void capturedLink_capturedLinkExpiresAfterClick() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor> openInBrowserCaptor =
ArgumentCaptor.forClass(Function1.class);
// Simulate menu opening and clicking open in browser button
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -761,7 +774,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
// Verify handle menu's browser link not set to captured link since link not valid after
// open in browser clicked
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -770,10 +783,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
public void capturedLink_openInBrowserListenerCalledOnClick() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor> openInBrowserCaptor =
ArgumentCaptor.forClass(Function1.class);
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -791,16 +805,30 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void genericLink_genericLinkUsedWhenCapturedLinkUnavailable() {
+ public void webUriLink_webUriLinkUsedWhenCapturedLinkUnavailable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, null /* captured link */, TEST_URI2 /* generic link */);
-
- // Verify handle menu's browser link set as generic link no captured link is available
- decor.createHandleMenu(mMockSplitScreenController);
+ taskInfo, null /* captured link */, TEST_URI2 /* web uri */,
+ TEST_URI3 /* generic link */);
+ // Verify handle menu's browser link set as web uri link when captured link is unavailable
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI2);
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+ public void genericLink_genericLinkUsedWhenCapturedLinkAndWebUriUnavailable() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ final DesktopModeWindowDecoration decor = createWindowDecoration(
+ taskInfo, null /* captured link */, null /* web uri */,
+ TEST_URI3 /* generic link */);
+
+ // Verify handle menu's browser link set as generic link when captured link and web uri link
+ // are unavailable
+ createHandleMenu(decor);
+ verifyHandleMenuCreated(TEST_URI3);
+ }
+
@Test
public void handleMenu_onCloseMenuClick_closesMenu() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -808,7 +836,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
true /* relayout */);
final ArgumentCaptor> closeClickListener =
ArgumentCaptor.forClass(Function0.class);
- decoration.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decoration);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -826,7 +854,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
private void verifyHandleMenuCreated(@Nullable Uri uri) {
- verify(mMockHandleMenuFactory).create(any(), anyInt(), any(), any(),
+ verify(mMockHandleMenuFactory).create(any(), any(), anyInt(), any(), any(),
any(), anyBoolean(), anyBoolean(), eq(uri), anyInt(), anyInt(), anyInt());
}
@@ -858,9 +886,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private DesktopModeWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, @Nullable Uri capturedLink,
- @Nullable Uri genericLink) {
+ @Nullable Uri webUri, @Nullable Uri genericLink) {
taskInfo.capturedLink = capturedLink;
taskInfo.capturedLinkTimestamp = System.currentTimeMillis();
+ mAssistContent.setWebUri(webUri);
final String genericLinkString = genericLink == null ? null : genericLink.toString();
doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any());
// Relayout to set captured link
@@ -892,10 +921,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
mContext, mMockDisplayController, mMockSplitScreenController,
mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mMockHandler, mBgExecutor,
mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
- mMockGenericLinksParser, SurfaceControl.Builder::new, mMockTransactionSupplier,
- WindowContainerTransaction::new, SurfaceControl::new,
- mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory,
- mMockMultiInstanceHelper);
+ mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
+ mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
+ new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
+ maximizeMenuFactory, mMockHandleMenuFactory, mMockMultiInstanceHelper);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
@@ -923,6 +952,13 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
+ private void createHandleMenu(@NonNull DesktopModeWindowDecoration decor) {
+ decor.createHandleMenu();
+ // Call DesktopModeWindowDecoration#onAssistContentReceived because decor waits to receive
+ // {@link AssistContent} before creating the menu
+ decor.onAssistContentReceived(mAssistContent);
+ }
+
private static boolean hasNoInputChannelFeature(RelayoutParams params) {
return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL)
!= 0;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 627dfe7185218828f0301a4a0e96d7b62389c956..100abbbb8332cb5ed973b4ac0767eb5b098b422e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -33,6 +33,7 @@ import android.view.LayoutInflater
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
import android.view.View
+import android.view.WindowManager
import androidx.core.graphics.toPointF
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags
@@ -77,6 +78,8 @@ class HandleMenuTest : ShellTestCase() {
@Mock
private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
@Mock
+ private lateinit var mockWindowManager: WindowManager
+ @Mock
private lateinit var onClickListener: View.OnClickListener
@Mock
private lateinit var onTouchListener: View.OnTouchListener
@@ -230,8 +233,9 @@ class HandleMenuTest : ShellTestCase() {
}
else -> error("Invalid windowing mode")
}
- val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId, appIcon, appName,
- splitScreenController, shouldShowWindowingPill = true,
+ val handleMenu = HandleMenu(mockDesktopWindowDecoration,
+ WindowManagerWrapper(mockWindowManager),
+ layoutId, appIcon, appName, splitScreenController, shouldShowWindowingPill = true,
shouldShowNewWindowButton = true,
null /* openInBrowserLink */, captionWidth = HANDLE_WIDTH, captionHeight = 50,
captionX = captionX
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
index 28b4eb6e8ab152595bc652c413daf97f59df47e2..0f52ed7f1c02e49a4f4a5d3041fb3434c2c4080d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
@@ -25,6 +25,7 @@ import android.view.WindowManager
import androidx.test.filters.SmallTest
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,6 +71,7 @@ class AdditionalSystemViewContainerTest : ShellTestCase() {
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
viewContainer = AdditionalSystemViewContainer(
mockContext,
+ WindowManagerWrapper(mockWindowManager),
TASK_ID,
X,
Y,
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 43a70c176a839d957c0b84fdd8836545da02fa19..5a4cff0c319e5e362a448ab5a7ed36b195d1c700 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -309,9 +309,8 @@ Asset::Asset(void)
return NULL;
}
- // We succeeded, so relinquish control of dataMap
pAsset->mAccessMode = mode;
- return std::move(pAsset);
+ return pAsset;
}
/*
@@ -328,9 +327,8 @@ Asset::Asset(void)
return NULL;
}
- // We succeeded, so relinquish control of dataMap
pAsset->mAccessMode = mode;
- return std::move(pAsset);
+ return pAsset;
}
/*
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index 09232b64616d79c94eef396afc960955fca998b2..a58493aa47cab3ead3bc61484b28c5b354789494 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -7,6 +7,17 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
+cc_library_headers {
+ name: "libhostgraphics_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
cc_library_host_static {
name: "libhostgraphics",
@@ -30,12 +41,13 @@ cc_library_host_static {
],
header_libs: [
+ "libhostgraphics_headers",
"libnativebase_headers",
"libnativedisplay_headers",
"libnativewindow_headers",
],
- export_include_dirs: ["."],
+ export_include_dirs: ["include"],
target: {
windows: {
diff --git a/libs/hostgraphics/gui/BufferItem.h b/libs/hostgraphics/include/gui/BufferItem.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferItem.h
rename to libs/hostgraphics/include/gui/BufferItem.h
diff --git a/libs/hostgraphics/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferItemConsumer.h
rename to libs/hostgraphics/include/gui/BufferItemConsumer.h
diff --git a/libs/hostgraphics/gui/BufferQueue.h b/libs/hostgraphics/include/gui/BufferQueue.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferQueue.h
rename to libs/hostgraphics/include/gui/BufferQueue.h
diff --git a/libs/hostgraphics/gui/ConsumerBase.h b/libs/hostgraphics/include/gui/ConsumerBase.h
similarity index 100%
rename from libs/hostgraphics/gui/ConsumerBase.h
rename to libs/hostgraphics/include/gui/ConsumerBase.h
diff --git a/libs/hostgraphics/gui/IGraphicBufferConsumer.h b/libs/hostgraphics/include/gui/IGraphicBufferConsumer.h
similarity index 100%
rename from libs/hostgraphics/gui/IGraphicBufferConsumer.h
rename to libs/hostgraphics/include/gui/IGraphicBufferConsumer.h
diff --git a/libs/hostgraphics/gui/IGraphicBufferProducer.h b/libs/hostgraphics/include/gui/IGraphicBufferProducer.h
similarity index 100%
rename from libs/hostgraphics/gui/IGraphicBufferProducer.h
rename to libs/hostgraphics/include/gui/IGraphicBufferProducer.h
diff --git a/libs/hostgraphics/gui/Surface.h b/libs/hostgraphics/include/gui/Surface.h
similarity index 100%
rename from libs/hostgraphics/gui/Surface.h
rename to libs/hostgraphics/include/gui/Surface.h
diff --git a/libs/hostgraphics/ui/Fence.h b/libs/hostgraphics/include/ui/Fence.h
similarity index 100%
rename from libs/hostgraphics/ui/Fence.h
rename to libs/hostgraphics/include/ui/Fence.h
diff --git a/libs/hostgraphics/ui/GraphicBuffer.h b/libs/hostgraphics/include/ui/GraphicBuffer.h
similarity index 100%
rename from libs/hostgraphics/ui/GraphicBuffer.h
rename to libs/hostgraphics/include/ui/GraphicBuffer.h
diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp
index 5f5ffe97e953267e0f8887adf57c57890d5a497a..27add3542c011e84b4b94a3295066039c23d8a76 100644
--- a/libs/hwui/AutoBackendTextureRelease.cpp
+++ b/libs/hwui/AutoBackendTextureRelease.cpp
@@ -17,11 +17,12 @@
#include "AutoBackendTextureRelease.h"
#include
-#include
-#include
-#include
#include
+#include
+#include
+#include
#include
+
#include "renderthread/RenderThread.h"
#include "utils/Color.h"
#include "utils/PaintUtils.h"
diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h
index f0eb2a8b6eab3dfcd87476c740eac92a5bbfe8ac..d58cd1787ee8e08344a934d6a570cedebdb5f20b 100644
--- a/libs/hwui/AutoBackendTextureRelease.h
+++ b/libs/hwui/AutoBackendTextureRelease.h
@@ -16,10 +16,10 @@
#pragma once
-#include
-#include
#include
#include
+#include
+#include
#include
namespace android {
diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h
index c1c30f5379ab43283b5b26a11c222e70c197e7f1..c0cedf12c0aed03e0f093d273746e975520077f7 100644
--- a/libs/hwui/FeatureFlags.h
+++ b/libs/hwui/FeatureFlags.h
@@ -25,14 +25,6 @@ namespace android {
namespace text_feature {
-inline bool fix_double_underline() {
-#ifdef __ANDROID__
- return com_android_text_flags_fix_double_underline();
-#else
- return true;
-#endif // __ANDROID__
-}
-
inline bool deprecate_ui_fonts() {
#ifdef __ANDROID__
return com_android_text_flags_deprecate_ui_fonts();
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 27ea150756823be6d95087ac5ab7991ce3bebc0d..236c3736816e0b5962294200b4e795110043d673 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -21,8 +21,6 @@
#include
#include
#include