Merge "Ensure face icon controller is deactivated when view is detached" into sc-v2-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 9eb7bb71..84d05c8 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2918,6 +2918,13 @@
reasonCode, reason).sendToTarget();
}
reportTempWhitelistChangedLocked(uid, true);
+ } else {
+ // The uid is already temp allowlisted, only need to update AMS for temp allowlist
+ // duration.
+ if (mLocalActivityManager != null) {
+ mLocalActivityManager.updateDeviceIdleTempAllowlist(null, uid, true,
+ duration, tempAllowListType, reasonCode, reason, callingUid);
+ }
}
}
if (informWhitelistChanged) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 591e8ba..4becc6b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1008,13 +1008,21 @@
}
@Override
- public void onUserUnlocked(@NonNull TargetUser user) {
+ public void onUserStarting(@NonNull TargetUser user) {
synchronized (mLock) {
- // Note that the user has started after its unlocked instead of when the user
- // actually starts because the storage won't be decrypted until unlock.
mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier());
}
- // Let's kick any outstanding jobs for this user.
+ // The user is starting but credential encrypted storage is still locked.
+ // Only direct-boot-aware jobs can safely run.
+ // Let's kick off any eligible jobs for this user.
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ }
+
+ @Override
+ public void onUserUnlocked(@NonNull TargetUser user) {
+ // The user is fully unlocked and credential encrypted storage is now decrypted.
+ // Direct-boot-UNaware jobs can now safely run.
+ // Let's kick off any outstanding jobs for this user.
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 8ba8c1a..9a16390 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1298,7 +1298,7 @@
field public static final int shortcutLongLabel = 16844074; // 0x101052a
field public static final int shortcutShortLabel = 16844073; // 0x1010529
field public static final int shouldDisableView = 16843246; // 0x10101ee
- field public static final int shouldUseDefaultUnfoldTransition;
+ field public static final int shouldUseDefaultDeviceStateChangeTransition;
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
field public static final int showDividers = 16843561; // 0x1010329
@@ -6927,7 +6927,7 @@
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
- method public boolean shouldUseDefaultUnfoldTransition();
+ method public boolean shouldUseDefaultDeviceStateChangeTransition();
method public boolean supportsMultipleDisplays();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
@@ -35275,7 +35275,6 @@
field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
field public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI = "android.provider.extra.SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI";
- field public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT = "android.provider.extra.SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT";
field public static final String EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY = "android.provider.extra.SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY";
field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
field public static final String EXTRA_WIFI_NETWORK_LIST = "android.provider.extra.WIFI_NETWORK_LIST";
@@ -39002,6 +39001,13 @@
package android.service.voice {
+ public final class VisibleActivityInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.service.voice.VoiceInteractionSession.ActivityId getActivityId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.VisibleActivityInfo> CREATOR;
+ }
+
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
method public int getDisabledShowContext();
@@ -39063,6 +39069,7 @@
method public void onTaskStarted(android.content.Intent, int);
method public void onTrimMemory(int);
method public final void performDirectAction(@NonNull android.app.DirectAction, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.os.Bundle>);
+ method public final void registerVisibleActivityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
method public final void requestDirectActions(@NonNull android.service.voice.VoiceInteractionSession.ActivityId, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.DirectAction>>);
method public void setContentView(android.view.View);
method public void setDisabledShowContext(int);
@@ -39072,6 +39079,7 @@
method public void show(android.os.Bundle, int);
method public void startAssistantActivity(android.content.Intent);
method public void startVoiceActivity(android.content.Intent);
+ method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4
@@ -39145,6 +39153,11 @@
method public boolean isActive();
}
+ public static interface VoiceInteractionSession.VisibleActivityCallback {
+ method public default void onInvisible(@NonNull android.service.voice.VoiceInteractionSession.ActivityId);
+ method public default void onVisible(@NonNull android.service.voice.VisibleActivityInfo);
+ }
+
public abstract class VoiceInteractionSessionService extends android.app.Service {
ctor public VoiceInteractionSessionService();
method public android.os.IBinder onBind(android.content.Intent);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 2ecf088..d484100 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2391,6 +2391,10 @@
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
}
+ public final class VisibleActivityInfo implements android.os.Parcelable {
+ ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
+ }
+
}
package android.service.watchdog {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4376d22..f53c5b6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -771,6 +771,11 @@
return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
}
+ /** @hide Should this process state be considered in the cache? */
+ public static final boolean isProcStateCached(int procState) {
+ return procState >= PROCESS_STATE_CACHED_ACTIVITY;
+ }
+
/** @hide Is this a foreground service type? */
public static boolean isForegroundService(int procState) {
return procState == PROCESS_STATE_FOREGROUND_SERVICE;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d5f51ad..f8c8aa3 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -156,6 +156,7 @@
/**
* Update information about which app IDs are on the temp allowlist.
* @param appids the updated list of appIds in temp allowlist.
+ * If null, it is to update only changingUid.
* @param changingUid uid to add or remove to temp allowlist.
* @param adding true to add to temp allowlist, false to remove from temp allowlist.
* @param durationMs when adding is true, the duration to be in temp allowlist.
@@ -165,7 +166,7 @@
* @param callingUid the callingUid that setup this temp allowlist, only valid when param adding
* is true.
*/
- public abstract void updateDeviceIdleTempAllowlist(int[] appids, int changingUid,
+ public abstract void updateDeviceIdleTempAllowlist(@Nullable int[] appids, int changingUid,
boolean adding, long durationMs, @TempAllowListType int type,
@ReasonCode int reasonCode,
@Nullable String reason, int callingUid);
@@ -651,4 +652,23 @@
public abstract int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);
+
+ /**
+ * Sets the provider to communicate between voice interaction manager service and
+ * ActivityManagerService.
+ */
+ public abstract void setVoiceInteractionManagerProvider(
+ @Nullable VoiceInteractionManagerProvider provider);
+
+ /**
+ * Provides the interface to communicate between voice interaction manager service and
+ * ActivityManagerService.
+ */
+ public interface VoiceInteractionManagerProvider {
+ /**
+ * Notifies the service when a high-level activity event has been changed, for example,
+ * an activity was resumed or stopped.
+ */
+ void notifyActivityEventChanged();
+ }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2e22b92..80554d7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3616,6 +3616,11 @@
}
activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
activity.mCalled = false;
+ // Assigning the activity to the record before calling onCreate() allows
+ // ActivityThread#getActivity() lookup for the callbacks triggered from
+ // ActivityLifecycleCallbacks#onActivityCreated() or
+ // ActivityLifecycleCallback#onActivityPostCreated().
+ r.activity = activity;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
@@ -3626,7 +3631,6 @@
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
- r.activity = activity;
mLastReportedWindowingMode.put(activity.getActivityToken(),
config.windowConfiguration.getWindowingMode());
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 719025f..61b1abe 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6506,25 +6506,34 @@
if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
&& !styleDisplaysCustomViewInline()) {
- if (mN.contentView == null) {
- mN.contentView = createContentView();
+ RemoteViews newContentView = mN.contentView;
+ RemoteViews newBigContentView = mN.bigContentView;
+ RemoteViews newHeadsUpContentView = mN.headsUpContentView;
+ if (newContentView == null) {
+ newContentView = createContentView();
mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
- mN.contentView.getSequenceNumber());
+ newContentView.getSequenceNumber());
}
- if (mN.bigContentView == null) {
- mN.bigContentView = createBigContentView();
- if (mN.bigContentView != null) {
+ if (newBigContentView == null) {
+ newBigContentView = createBigContentView();
+ if (newBigContentView != null) {
mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
- mN.bigContentView.getSequenceNumber());
+ newBigContentView.getSequenceNumber());
}
}
- if (mN.headsUpContentView == null) {
- mN.headsUpContentView = createHeadsUpContentView();
- if (mN.headsUpContentView != null) {
+ if (newHeadsUpContentView == null) {
+ newHeadsUpContentView = createHeadsUpContentView();
+ if (newHeadsUpContentView != null) {
mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
- mN.headsUpContentView.getSequenceNumber());
+ newHeadsUpContentView.getSequenceNumber());
}
}
+ // Don't set any of the content views until after they have all been generated,
+ // to avoid the generated .contentView triggering the logic which skips generating
+ // the .bigContentView.
+ mN.contentView = newContentView;
+ mN.bigContentView = newBigContentView;
+ mN.headsUpContentView = newHeadsUpContentView;
}
if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 4dff4e0..c552cb6 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -81,7 +81,7 @@
final int mContextDescriptionResource;
final boolean mShowMetadataInPreview;
final boolean mSupportsAmbientMode;
- final boolean mShouldUseDefaultUnfoldTransition;
+ final boolean mShouldUseDefaultDeviceStateChangeTransition;
final String mSettingsSliceUri;
final boolean mSupportMultipleDisplays;
@@ -146,9 +146,9 @@
mSupportsAmbientMode = sa.getBoolean(
com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
false);
- mShouldUseDefaultUnfoldTransition = sa.getBoolean(
- com.android.internal.R.styleable.Wallpaper_shouldUseDefaultUnfoldTransition,
- true);
+ mShouldUseDefaultDeviceStateChangeTransition = sa.getBoolean(
+ com.android.internal.R.styleable
+ .Wallpaper_shouldUseDefaultDeviceStateChangeTransition, true);
mSettingsSliceUri = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
mSupportMultipleDisplays = sa.getBoolean(
@@ -175,7 +175,7 @@
mSupportsAmbientMode = source.readInt() != 0;
mSettingsSliceUri = source.readString();
mSupportMultipleDisplays = source.readInt() != 0;
- mShouldUseDefaultUnfoldTransition = source.readInt() != 0;
+ mShouldUseDefaultDeviceStateChangeTransition = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -399,23 +399,25 @@
}
/**
- * Returns whether this wallpaper should receive default zooming updates when unfolding.
- * If set to false the wallpaper will not receive zoom events when folding or unfolding
- * a foldable device, so it can implement its own unfold transition.
+ * Returns whether this wallpaper should receive default zooming updates when the device
+ * changes its state (e.g. when folding or unfolding a foldable device).
+ * If set to false the wallpaper will not receive zoom events when changing the device state,
+ * so it can implement its own transition instead.
* <p>
* This corresponds to the value {@link
- * android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition} in the XML description
- * of the wallpaper.
+ * android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition} in the
+ * XML description of the wallpaper.
* <p>
* The default value is {@code true}.
*
- * @see android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition
- * @return {@code true} if wallpaper should receive default fold/unfold transition updates
+ * @see android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition
+ * @return {@code true} if wallpaper should receive default device state change
+ * transition updates
*
- * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition
+ * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition
*/
- public boolean shouldUseDefaultUnfoldTransition() {
- return mShouldUseDefaultUnfoldTransition;
+ public boolean shouldUseDefaultDeviceStateChangeTransition() {
+ return mShouldUseDefaultDeviceStateChangeTransition;
}
public void dump(Printer pw, String prefix) {
@@ -448,7 +450,7 @@
dest.writeInt(mSupportsAmbientMode ? 1 : 0);
dest.writeString(mSettingsSliceUri);
dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
- dest.writeInt(mShouldUseDefaultUnfoldTransition ? 1 : 0);
+ dest.writeInt(mShouldUseDefaultDeviceStateChangeTransition ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 08e95a2..c3ec094 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5935,6 +5935,10 @@
* more general access to the URI's content provider then this check will
* always fail.
*
+ * <strong>Note:</strong> On SDK Version {@link android.os.Build.VERSION_CODES#S},
+ * calling this method from a secondary-user's context will incorrectly return
+ * {@link PackageManager#PERMISSION_DENIED} for all {code uris}.
+ *
* @param uris The list of URIs that is being checked.
* @param pid The process ID being checked against. Must be > 0.
* @param uid The user ID being checked against. A uid of 0 is the root
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9e35a32..35794d7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2738,6 +2738,22 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
+ /**
+ * Broadcast Action: One of the suspend conditions have been modified for the packages.
+ * <p>Includes the following extras:
+ * <ul>
+ * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been modified
+ * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been modified
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system. It is only sent to registered receivers.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGES_SUSPENSION_CHANGED =
+ "android.intent.action.PACKAGES_SUSPENSION_CHANGED";
/**
* Broadcast Action: Distracting packages have been changed.
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 5cfba3d..395c655 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -263,12 +263,12 @@
@Override
public void onServiceConnected(ComponentName component, IBinder binder) {
mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder);
- mInitFuture.setStatus(true);
try {
mSupportsAdvancedExtensions = mProxy.advancedExtensionsSupported();
} catch (RemoteException e) {
Log.e(TAG, "Remote IPC failed!");
}
+ mInitFuture.setStatus(true);
}
};
ctx.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT |
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 3b1cb94..425f22c 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -58,7 +58,7 @@
private static final class JpegParameters {
public HashSet<Long> mTimeStamps = new HashSet<>();
- public int mRotation = JPEG_DEFAULT_ROTATION; // CCW multiple of 90 degrees
+ public int mRotation = JPEG_DEFAULT_ROTATION; // CW multiple of 90 degrees
public int mQuality = JPEG_DEFAULT_QUALITY; // [0..100]
}
@@ -100,7 +100,8 @@
Integer orientation = captureBundles.get(0).captureResult.get(
CaptureResult.JPEG_ORIENTATION);
if (orientation != null) {
- ret.mRotation = orientation / 90;
+ // The jpeg encoder expects CCW rotation, convert from CW
+ ret.mRotation = (360 - (orientation % 360)) / 90;
} else {
Log.w(TAG, "No jpeg rotation set, using default: " + JPEG_DEFAULT_ROTATION);
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 73961ff..d8f2039 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1288,6 +1288,23 @@
*/
String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
"fixed_refresh_rate_high_ambient_brightness_thresholds";
+
+ /**
+ * Key for refresh rate when the device is in high brightness mode for sunlight visility.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+ * @see android.R.integer#config_defaultRefreshRateInHbmSunlight
+ */
+ String KEY_REFRESH_RATE_IN_HBM_SUNLIGHT = "refresh_rate_in_hbm_sunlight";
+
+ /**
+ * Key for refresh rate when the device is in high brightness mode for HDR.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+ * @see android.R.integer#config_defaultRefreshRateInHbmHdr
+ */
+ String KEY_REFRESH_RATE_IN_HBM_HDR = "refresh_rate_in_hbm_hdr";
+
/**
* Key for default peak refresh rate
*
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index f483752..0f94cbe 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -271,6 +271,16 @@
}
/**
+ * Returns the names of custom power components in order, so the first name in the array
+ * corresponds to the custom componentId
+ * {@link BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID}.
+ */
+ @NonNull
+ public String[] getCustomPowerComponentNames() {
+ return mCustomPowerComponentNames;
+ }
+
+ /**
* Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s.
*/
@NonNull
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 3d466a0..c646623 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -74,7 +74,7 @@
private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
// Debuggable builds will throw an AssertionError if the number of map entries exceeds:
- private static final int CRASH_AT_SIZE = 20_000;
+ private static final int CRASH_AT_SIZE = 25_000;
/**
* We next warn when we exceed this bucket size.
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3aa0bcb..4dae7c7 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -548,6 +548,7 @@
WAKE_REASON_HDMI,
WAKE_REASON_DISPLAY_GROUP_ADDED,
WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
+ WAKE_REASON_UNFOLD_DEVICE
})
@Retention(RetentionPolicy.SOURCE)
public @interface WakeReason{}
@@ -647,6 +648,12 @@
public static final int WAKE_REASON_DISPLAY_GROUP_TURNED_ON = 11;
/**
+ * Wake up reason code: Waking the device due to unfolding of a foldable device.
+ * @hide
+ */
+ public static final int WAKE_REASON_UNFOLD_DEVICE = 12;
+
+ /**
* Convert the wake reason to a string for debugging purposes.
* @hide
*/
@@ -664,6 +671,7 @@
case WAKE_REASON_LID: return "WAKE_REASON_LID";
case WAKE_REASON_DISPLAY_GROUP_ADDED: return "WAKE_REASON_DISPLAY_GROUP_ADDED";
case WAKE_REASON_DISPLAY_GROUP_TURNED_ON: return "WAKE_REASON_DISPLAY_GROUP_TURNED_ON";
+ case WAKE_REASON_UNFOLD_DEVICE: return "WAKE_REASON_UNFOLD_DEVICE";
default: return Integer.toString(wakeReason);
}
}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 54905ec..8928a42 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.os.IVold;
import java.util.List;
@@ -135,4 +136,19 @@
* {@link VolumeInfo#isPrimary()}
*/
public abstract List<String> getPrimaryVolumeIds();
+
+ /**
+ * Tells StorageManager that CE storage for this user has been prepared.
+ *
+ * @param userId userId for which CE storage has been prepared
+ */
+ public abstract void markCeStoragePrepared(@UserIdInt int userId);
+
+ /**
+ * Returns true when CE storage for this user has been prepared.
+ *
+ * When the user key is unlocked and CE storage has been prepared,
+ * it's ok to access and modify CE directories on volumes for this user.
+ */
+ public abstract boolean isCeStoragePrepared(@UserIdInt int userId);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1bdfdc2..7472cca 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -35,7 +35,6 @@
import android.app.AutomaticZenRule;
import android.app.NotificationChannel;
import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.app.SearchManager;
import android.app.WallpaperManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -2244,6 +2243,21 @@
public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
/**
+ * Activity Action: Show screen that lets user configure wifi tethering.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+ * <p>
+ * Input: Nothing
+ * <p>
+ * Output: Nothing
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_WIFI_TETHER_SETTING =
+ "com.android.settings.WIFI_TETHER_SETTINGS";
+
+ /**
* Broadcast to trigger notification of asking user to enable MMS.
* Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}.
*
@@ -16961,9 +16975,6 @@
*
* Input: {@link #EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY} must be included to
* specify a key that indicates the menu item which will be highlighted on settings home menu.
- *
- * Input: {@link #EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT} is optional. Apps
- * can use the {@link PendingIntent} extra to launch into its private {@link Activity}.
* <p>
* Output: Nothing.
*/
@@ -16991,15 +17002,6 @@
"android.provider.extra.SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY";
/**
- * Activity Extra: Apps can use the {@link PendingIntent} extra to launch into its private
- * {@link Activity}.
- * <p>
- * This is an optional extra field to {@link #ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK}.
- */
- public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT =
- "android.provider.extra.SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT";
-
- /**
* Performs a strict and comprehensive check of whether a calling package is allowed to
* write/modify system settings, as the condition differs for pre-M, M+, and
* privileged/preinstalled apps. If the provided uid does not match the
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index c142a53..59f1e8e 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -22,6 +22,7 @@
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.IBinder;
+import android.service.voice.VisibleActivityInfo;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
@@ -39,4 +40,5 @@
void closeSystemDialogs();
void onLockscreenShown();
void destroy();
+ void updateVisibleActivityInfo(in VisibleActivityInfo visibleActivityInfo, int type);
}
diff --git a/core/java/android/service/voice/VisibleActivityInfo.aidl b/core/java/android/service/voice/VisibleActivityInfo.aidl
new file mode 100644
index 0000000..34bd57c
--- /dev/null
+++ b/core/java/android/service/voice/VisibleActivityInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+parcelable VisibleActivityInfo;
diff --git a/core/java/android/service/voice/VisibleActivityInfo.java b/core/java/android/service/voice/VisibleActivityInfo.java
new file mode 100644
index 0000000..139544c
--- /dev/null
+++ b/core/java/android/service/voice/VisibleActivityInfo.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * The class is used to represent a visible activity information. The system provides this to
+ * services that need to know {@link android.service.voice.VoiceInteractionSession.ActivityId}.
+ */
+@DataClass(
+ genConstructor = false,
+ genEqualsHashCode = true,
+ genHiddenConstDefs = false,
+ genGetters = false,
+ genToString = true
+)
+public final class VisibleActivityInfo implements Parcelable {
+
+ /**
+ * Indicates that it is a new visible activity.
+ *
+ * @hide
+ */
+ public static final int TYPE_ACTIVITY_ADDED = 1;
+
+ /**
+ * Indicates that it has become a invisible activity.
+ *
+ * @hide
+ */
+ public static final int TYPE_ACTIVITY_REMOVED = 2;
+
+ /**
+ * The identifier of the task this activity is in.
+ */
+ private final int mTaskId;
+
+ /**
+ * Token for targeting this activity for assist purposes.
+ */
+ @NonNull
+ private final IBinder mAssistToken;
+
+ /** @hide */
+ @TestApi
+ public VisibleActivityInfo(
+ int taskId,
+ @NonNull IBinder assistToken) {
+ Objects.requireNonNull(assistToken);
+ mTaskId = taskId;
+ mAssistToken = assistToken;
+ }
+
+ /**
+ * Returns the {@link android.service.voice.VoiceInteractionSession.ActivityId} of this
+ * visible activity which can be used to interact with an activity, for example through
+ * {@link VoiceInteractionSession#requestDirectActions(VoiceInteractionSession.ActivityId,
+ * CancellationSignal, Executor, Consumer)}.
+ */
+ public @NonNull VoiceInteractionSession.ActivityId getActivityId() {
+ return new VoiceInteractionSession.ActivityId(mTaskId, mAssistToken);
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/VisibleActivityInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "VisibleActivityInfo { " +
+ "taskId = " + mTaskId + ", " +
+ "assistToken = " + mAssistToken +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(VisibleActivityInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ VisibleActivityInfo that = (VisibleActivityInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mTaskId == that.mTaskId
+ && Objects.equals(mAssistToken, that.mAssistToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mTaskId;
+ _hash = 31 * _hash + Objects.hashCode(mAssistToken);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mTaskId);
+ dest.writeStrongBinder(mAssistToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ VisibleActivityInfo(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int taskId = in.readInt();
+ IBinder assistToken = (IBinder) in.readStrongBinder();
+
+ this.mTaskId = taskId;
+ this.mAssistToken = assistToken;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAssistToken);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<VisibleActivityInfo> CREATOR
+ = new Parcelable.Creator<VisibleActivityInfo>() {
+ @Override
+ public VisibleActivityInfo[] newArray(int size) {
+ return new VisibleActivityInfo[size];
+ }
+
+ @Override
+ public VisibleActivityInfo createFromParcel(@NonNull Parcel in) {
+ return new VisibleActivityInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1632383555284L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/voice/VisibleActivityInfo.java",
+ inputSignatures = "public static final int TYPE_ACTIVITY_ADDED\npublic static final int TYPE_ACTIVITY_REMOVED\nprivate final int mTaskId\nprivate final @android.annotation.NonNull android.os.IBinder mAssistToken\npublic @android.annotation.NonNull android.service.voice.VoiceInteractionSession.ActivityId getActivityId()\nclass VisibleActivityInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genEqualsHashCode=true, genHiddenConstDefs=false, genGetters=false, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index c048286..c806409 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -22,7 +22,6 @@
import com.android.internal.annotations.Immutable;
-
/**
* @hide
* Private interface to the VoiceInteractionManagerService for use by ActivityManagerService.
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 725e20f..9db856a 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -74,9 +74,11 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -177,6 +179,10 @@
ICancellationSignal mKillCallback;
+ private final Map<VisibleActivityCallback, Executor> mVisibleActivityCallbacks =
+ new ArrayMap<>();
+ private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
+
final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
@Override
public IVoiceInteractorRequest startConfirmation(String callingPackage,
@@ -352,6 +358,13 @@
public void destroy() {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
}
+
+ @Override
+ public void updateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) {
+ mHandlerCaller.sendMessage(
+ mHandlerCaller.obtainMessageIO(MSG_UPDATE_VISIBLE_ACTIVITY_INFO, type,
+ visibleActivityInfo));
+ }
};
/**
@@ -843,6 +856,9 @@
static final int MSG_SHOW = 106;
static final int MSG_HIDE = 107;
static final int MSG_ON_LOCKSCREEN_SHOWN = 108;
+ static final int MSG_UPDATE_VISIBLE_ACTIVITY_INFO = 109;
+ static final int MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK = 110;
+ static final int MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK = 111;
class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
@Override
@@ -928,6 +944,27 @@
if (DEBUG) Log.d(TAG, "onLockscreenShown");
onLockscreenShown();
break;
+ case MSG_UPDATE_VISIBLE_ACTIVITY_INFO:
+ if (DEBUG) {
+ Log.d(TAG, "doUpdateVisibleActivityInfo: visibleActivityInfo=" + msg.obj
+ + " type=" + msg.arg1);
+ }
+ doUpdateVisibleActivityInfo((VisibleActivityInfo) msg.obj, msg.arg1);
+ break;
+ case MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK:
+ if (DEBUG) {
+ Log.d(TAG, "doRegisterVisibleActivityCallback");
+ }
+ args = (SomeArgs) msg.obj;
+ doRegisterVisibleActivityCallback((Executor) args.arg1,
+ (VisibleActivityCallback) args.arg2);
+ break;
+ case MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK:
+ if (DEBUG) {
+ Log.d(TAG, "doUnregisterVisibleActivityCallback");
+ }
+ doUnregisterVisibleActivityCallback((VisibleActivityCallback) msg.obj);
+ break;
}
if (args != null) {
args.recycle();
@@ -1122,6 +1159,86 @@
}
}
+ private void doUpdateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) {
+
+ if (mVisibleActivityCallbacks.isEmpty()) {
+ return;
+ }
+
+ switch (type) {
+ case VisibleActivityInfo.TYPE_ACTIVITY_ADDED:
+ informVisibleActivityChanged(visibleActivityInfo, type);
+ mVisibleActivityInfos.add(visibleActivityInfo);
+ break;
+ case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED:
+ informVisibleActivityChanged(visibleActivityInfo, type);
+ mVisibleActivityInfos.remove(visibleActivityInfo);
+ break;
+ }
+ }
+
+ private void doRegisterVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull VisibleActivityCallback callback) {
+ if (mVisibleActivityCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.d(TAG, "doRegisterVisibleActivityCallback: callback has registered");
+ }
+ return;
+ }
+
+ int preCallbackCount = mVisibleActivityCallbacks.size();
+ mVisibleActivityCallbacks.put(callback, executor);
+
+ if (preCallbackCount == 0) {
+ try {
+ mSystemService.startListeningVisibleActivityChanged(mToken);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ } else {
+ for (int i = 0; i < mVisibleActivityInfos.size(); i++) {
+ final VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfos.get(i);
+ executor.execute(() -> callback.onVisible(visibleActivityInfo));
+ }
+ }
+ }
+
+ private void doUnregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) {
+ mVisibleActivityCallbacks.remove(callback);
+
+ if (mVisibleActivityCallbacks.size() == 0) {
+ mVisibleActivityInfos.clear();
+ try {
+ mSystemService.stopListeningVisibleActivityChanged(mToken);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private void informVisibleActivityChanged(VisibleActivityInfo visibleActivityInfo, int type) {
+ for (Map.Entry<VisibleActivityCallback, Executor> e :
+ mVisibleActivityCallbacks.entrySet()) {
+ final Executor executor = e.getValue();
+ final VisibleActivityCallback visibleActivityCallback = e.getKey();
+
+ switch (type) {
+ case VisibleActivityInfo.TYPE_ACTIVITY_ADDED:
+ Binder.withCleanCallingIdentity(() -> {
+ executor.execute(
+ () -> visibleActivityCallback.onVisible(visibleActivityInfo));
+ });
+ break;
+ case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED:
+ Binder.withCleanCallingIdentity(() -> {
+ executor.execute(() -> visibleActivityCallback.onInvisible(
+ visibleActivityInfo.getActivityId()));
+ });
+ break;
+ }
+ }
+ }
+
void ensureWindowCreated() {
if (mInitialized) {
return;
@@ -1926,6 +2043,45 @@
}
/**
+ * Registers a callback that will be notified when visible activities have been changed.
+ *
+ * @param executor The handler to receive the callback.
+ * @param callback The callback to receive the response.
+ *
+ * @throws IllegalStateException if calling this method before onCreate().
+ */
+ public final void registerVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull VisibleActivityCallback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "registerVisibleActivityCallback");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ mHandlerCaller.sendMessage(
+ mHandlerCaller.obtainMessageOO(MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK, executor,
+ callback));
+ }
+
+ /**
+ * Unregisters the callback.
+ *
+ * @param callback The callback to receive the response.
+ */
+ public final void unregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "unregisterVisibleActivityCallback");
+ }
+ Objects.requireNonNull(callback);
+
+ mHandlerCaller.sendMessage(
+ mHandlerCaller.obtainMessageO(MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK, callback));
+ }
+
+ /**
* Print the Service's state into the given stream. This gets invoked by
* {@link VoiceInteractionSessionService} when its Service
* {@link android.app.Service#dump} method is called.
@@ -1975,6 +2131,17 @@
}
/**
+ * Callback interface for receiving visible activity changes used for assistant usage.
+ */
+ public interface VisibleActivityCallback {
+ /** Callback to inform that an activity has become visible. */
+ default void onVisible(@NonNull VisibleActivityInfo activityInfo) {}
+
+ /** Callback to inform that a visible activity has gone. */
+ default void onInvisible(@NonNull ActivityId activityId) {}
+ }
+
+ /**
* Represents assist state captured when this session was started.
* It contains the various assist data objects and a reference to
* the source activity.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index cb5e51c..2d263a5 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -561,12 +561,16 @@
*/
public void reportEngineShown(boolean waitForEngineShown) {
if (mIWallpaperEngine.mShownReported) return;
- Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
if (!waitForEngineShown) {
+ Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
mCaller.removeMessages(MSG_REPORT_SHOWN);
mCaller.sendMessage(message);
} else {
- mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(1));
+ // if we are already waiting, no need to reset the timeout.
+ if (!mCaller.hasMessages(MSG_REPORT_SHOWN)) {
+ Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
+ mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(5));
+ }
}
}
@@ -2256,6 +2260,8 @@
mShownReported = true;
try {
mConnection.engineShown(this);
+ Log.d(TAG, "Wallpaper has updated the surface:"
+ + mWallpaperManager.getWallpaperInfo());
} catch (RemoteException e) {
Log.w(TAG, "Wallpaper host disappeared", e);
return;
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 362ea8c..5e647a4 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -115,7 +115,7 @@
@NonNull AttributionSource attributionSource) {
try {
if (mCurrentCallback == null) {
- boolean preflightPermissionCheckPassed = checkPermissionForPreflight(
+ boolean preflightPermissionCheckPassed = checkPermissionForPreflightNotHardDenied(
attributionSource);
if (preflightPermissionCheckPassed) {
if (DBG) {
@@ -470,10 +470,11 @@
return mStartedDataDelivery;
}
- private boolean checkPermissionForPreflight(AttributionSource attributionSource) {
- return PermissionChecker.checkPermissionForPreflight(RecognitionService.this,
- Manifest.permission.RECORD_AUDIO, attributionSource)
- == PermissionChecker.PERMISSION_GRANTED;
+ private boolean checkPermissionForPreflightNotHardDenied(AttributionSource attributionSource) {
+ int result = PermissionChecker.checkPermissionForPreflight(RecognitionService.this,
+ Manifest.permission.RECORD_AUDIO, attributionSource);
+ return result == PermissionChecker.PERMISSION_GRANTED
+ || result == PermissionChecker.PERMISSION_SOFT_DENIED;
}
void finishDataDelivery() {
diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java
index 80387aa..43d186e 100644
--- a/core/java/android/text/method/TranslationTransformationMethod.java
+++ b/core/java/android/text/method/TranslationTransformationMethod.java
@@ -62,6 +62,13 @@
return mOriginalTranslationMethod;
}
+ /**
+ * Returns the {@link TextView}'s {@link ViewTranslationResponse}.
+ */
+ public ViewTranslationResponse getViewTranslationResponse() {
+ return mTranslationResponse;
+ }
+
@Override
public CharSequence getTransformation(CharSequence source, View view) {
if (!mAllowLengthChanges) {
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 971e161..aecde44 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -165,6 +165,10 @@
return start + (stop - start) * amount;
}
+ public static float lerp(int start, int stop, float amount) {
+ return lerp((float) start, (float) stop, amount);
+ }
+
/**
* Returns the interpolation scalar (s) that satisfies the equation: {@code value = }{@link
* #lerp}{@code (a, b, s)}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index a6abed0..9da5088 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -175,6 +175,11 @@
float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data);
/**
+ * Drops the content of the current drag operation for accessibility
+ */
+ boolean dropForAccessibility(IWindow window, int x, int y);
+
+ /**
* Report the result of a drop action targeted to the given window.
* consumed is 'true' when the drop was accepted by a valid recipient,
* 'false' otherwise.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1ecdb90..b729c9f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3515,6 +3515,7 @@
* 1 PFLAG4_ALLOW_CLICK_WHEN_DISABLED
* 1 PFLAG4_DETACHED
* 1 PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE
+ * 1 PFLAG4_DRAG_A11Y_STARTED
* |-------|-------|-------|-------|
*/
@@ -3586,6 +3587,11 @@
*/
private static final int PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE = 0x000004000;
+ /**
+ * Indicates that the view has started a drag with {@link AccessibilityAction#ACTION_DRAG_START}
+ */
+ private static final int PFLAG4_DRAG_A11Y_STARTED = 0x000008000;
+
/* End of masks for mPrivateFlags4 */
/** @hide */
@@ -10384,8 +10390,17 @@
if (mTouchDelegate != null) {
info.setTouchDelegateInfo(mTouchDelegate.getTouchDelegateInfo());
}
+
+ if (startedSystemDragForAccessibility()) {
+ info.addAction(AccessibilityAction.ACTION_DRAG_CANCEL);
+ }
+
+ if (canAcceptAccessibilityDrop()) {
+ info.addAction(AccessibilityAction.ACTION_DRAG_DROP);
+ }
}
+
/**
* Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the
* additional data.
@@ -14222,9 +14237,45 @@
return true;
}
}
+
+ if (action == R.id.accessibilityActionDragDrop) {
+ if (!canAcceptAccessibilityDrop()) {
+ return false;
+ }
+ try {
+ if (mAttachInfo != null && mAttachInfo.mSession != null) {
+ final int[] location = new int[2];
+ getLocationInWindow(location);
+ final int centerX = location[0] + getWidth() / 2;
+ final int centerY = location[1] + getHeight() / 2;
+ return mAttachInfo.mSession.dropForAccessibility(mAttachInfo.mWindow,
+ centerX, centerY);
+ }
+ } catch (RemoteException e) {
+ Log.e(VIEW_LOG_TAG, "Unable to drop for accessibility", e);
+ }
+ return false;
+ } else if (action == R.id.accessibilityActionDragCancel) {
+ if (!startedSystemDragForAccessibility()) {
+ return false;
+ }
+ if (mAttachInfo != null && mAttachInfo.mDragToken != null) {
+ cancelDragAndDrop();
+ return true;
+ }
+ return false;
+ }
return false;
}
+ private boolean canAcceptAccessibilityDrop() {
+ if (!canAcceptDrag()) {
+ return false;
+ }
+ ListenerInfo li = mListenerInfo;
+ return (li != null) && (li.mOnDragListener != null || li.mOnReceiveContentListener != null);
+ }
+
private boolean traverseAtGranularity(int granularity, boolean forward,
boolean extendSelection) {
CharSequence text = getIterableTextForAccessibility();
@@ -20838,13 +20889,14 @@
mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;
}
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
+
mAttachInfo = null;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchDetachedFromWindow();
}
notifyEnterOrExitForAutoFillIfNeeded(false);
- notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
}
/**
@@ -26664,6 +26716,37 @@
data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
}
+ Rect bounds = new Rect();
+ getBoundsOnScreen(bounds, true);
+
+ Point lastTouchPoint = new Point();
+ mAttachInfo.mViewRootImpl.getLastTouchPoint(lastTouchPoint);
+ final ViewRootImpl root = mAttachInfo.mViewRootImpl;
+
+ // Skip surface logic since shadows and animation are not required during the a11y drag
+ final boolean a11yEnabled = AccessibilityManager.getInstance(mContext).isEnabled();
+ if (a11yEnabled && (flags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
+ try {
+ IBinder token = mAttachInfo.mSession.performDrag(
+ mAttachInfo.mWindow, flags, null,
+ mAttachInfo.mViewRootImpl.getLastTouchSource(),
+ 0f, 0f, 0f, 0f, data);
+ if (ViewDebug.DEBUG_DRAG) {
+ Log.d(VIEW_LOG_TAG, "startDragAndDrop via a11y action returned " + token);
+ }
+ if (token != null) {
+ root.setLocalDragState(myLocalState);
+ mAttachInfo.mDragToken = token;
+ mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this);
+ setAccessibilityDragStarted(true);
+ }
+ return token != null;
+ } catch (Exception e) {
+ Log.e(VIEW_LOG_TAG, "Unable to initiate a11y drag", e);
+ return false;
+ }
+ }
+
Point shadowSize = new Point();
Point shadowTouchPoint = new Point();
shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
@@ -26688,7 +26771,6 @@
+ " shadowX=" + shadowTouchPoint.x + " shadowY=" + shadowTouchPoint.y);
}
- final ViewRootImpl root = mAttachInfo.mViewRootImpl;
final SurfaceSession session = new SurfaceSession();
final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
.setName("drag surface")
@@ -26711,12 +26793,9 @@
surface.unlockCanvasAndPost(canvas);
}
- // repurpose 'shadowSize' for the last touch point
- root.getLastTouchPoint(shadowSize);
-
- token = mAttachInfo.mSession.performDrag(
- mAttachInfo.mWindow, flags, surfaceControl, root.getLastTouchSource(),
- shadowSize.x, shadowSize.y, shadowTouchPoint.x, shadowTouchPoint.y, data);
+ token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
+ root.getLastTouchSource(), lastTouchPoint.x, lastTouchPoint.y,
+ shadowTouchPoint.x, shadowTouchPoint.y, data);
if (ViewDebug.DEBUG_DRAG) {
Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
}
@@ -26728,6 +26807,10 @@
mAttachInfo.mDragToken = token;
// Cache the local state object for delivery with DragEvents
root.setLocalDragState(myLocalState);
+ if (a11yEnabled) {
+ // Set for AccessibilityEvents
+ mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this);
+ }
}
return token != null;
} catch (Exception e) {
@@ -26741,6 +26824,24 @@
}
}
+ void setAccessibilityDragStarted(boolean started) {
+ int pflags4 = mPrivateFlags4;
+ if (started) {
+ pflags4 |= PFLAG4_DRAG_A11Y_STARTED;
+ } else {
+ pflags4 &= ~PFLAG4_DRAG_A11Y_STARTED;
+ }
+
+ if (pflags4 != mPrivateFlags4) {
+ mPrivateFlags4 = pflags4;
+ sendWindowContentChangedAccessibilityEvent(CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+ }
+
+ private boolean startedSystemDragForAccessibility() {
+ return (mPrivateFlags4 & PFLAG4_DRAG_A11Y_STARTED) != 0;
+ }
+
/**
* Cancels an ongoing drag and drop operation.
* <p>
@@ -26958,6 +27059,12 @@
}
switch (event.mAction) {
+ case DragEvent.ACTION_DRAG_STARTED: {
+ if (result && li.mOnDragListener != null) {
+ sendWindowContentChangedAccessibilityEvent(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+ } break;
case DragEvent.ACTION_DRAG_ENTERED: {
mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
refreshDrawableState();
@@ -26966,7 +27073,15 @@
mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
refreshDrawableState();
} break;
+ case DragEvent.ACTION_DROP: {
+ if (result && (li.mOnDragListener != null | li.mOnReceiveContentListener != null)) {
+ sendWindowContentChangedAccessibilityEvent(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_DROPPED);
+ }
+ } break;
case DragEvent.ACTION_DRAG_ENDED: {
+ sendWindowContentChangedAccessibilityEvent(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
mPrivateFlags2 &= ~View.DRAG_MASK;
refreshDrawableState();
} break;
@@ -26979,6 +27094,15 @@
return (mPrivateFlags2 & PFLAG2_DRAG_CAN_ACCEPT) != 0;
}
+ void sendWindowContentChangedAccessibilityEvent(int changeType) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(changeType);
+ sendAccessibilityEventUnchecked(event);
+ }
+ }
+
/**
* This needs to be a better API (NOT ON VIEW) before it is exposed. If
* it is ever exposed at all.
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 775c15e..49f5229 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -412,6 +412,9 @@
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_STARTED}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_CANCELLED}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_DROPPED}
* </ul>
*/
public void notifySubtreeAccessibilityStateChanged(
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e38b883..c45b27a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -651,6 +651,7 @@
/* Drag/drop */
ClipDescription mDragDescription;
View mCurrentDragView;
+ View mStartedDragViewForA11y;
volatile Object mLocalDragState;
final PointF mDragPoint = new PointF();
final PointF mLastTouchPoint = new PointF();
@@ -2863,8 +2864,13 @@
}
}
+ final boolean surfaceControlChanged =
+ (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
+ == RELAYOUT_RES_SURFACE_CHANGED;
+
if (mSurfaceControl.isValid()) {
- updateOpacity(mWindowAttributes, dragResizing);
+ updateOpacity(mWindowAttributes, dragResizing,
+ surfaceControlChanged /*forceUpdate */);
}
if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
@@ -2899,9 +2905,7 @@
// RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new
// SurfaceControl.
surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId()
- || (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
- == RELAYOUT_RES_SURFACE_CHANGED)
- && mSurface.isValid();
+ || surfaceControlChanged) && mSurface.isValid();
if (surfaceReplaced) {
mSurfaceSequenceId++;
}
@@ -7584,6 +7588,11 @@
if (what == DragEvent.ACTION_DRAG_STARTED) {
mCurrentDragView = null; // Start the current-recipient tracking
mDragDescription = event.mClipDescription;
+ if (mStartedDragViewForA11y != null) {
+ // Send a drag started a11y event
+ mStartedDragViewForA11y.sendWindowContentChangedAccessibilityEvent(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_STARTED);
+ }
} else {
if (what == DragEvent.ACTION_DRAG_ENDED) {
mDragDescription = null;
@@ -7658,6 +7667,16 @@
// When the drag operation ends, reset drag-related state
if (what == DragEvent.ACTION_DRAG_ENDED) {
+ if (mStartedDragViewForA11y != null) {
+ // If the drag failed, send a cancelled event from the source. Otherwise,
+ // the View that accepted the drop sends CONTENT_CHANGE_TYPE_DRAG_DROPPED
+ if (!event.getResult()) {
+ mStartedDragViewForA11y.sendWindowContentChangedAccessibilityEvent(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_CANCELLED);
+ }
+ mStartedDragViewForA11y.setAccessibilityDragStarted(false);
+ }
+ mStartedDragViewForA11y = null;
mCurrentDragView = null;
setLocalDragState(null);
mAttachInfo.mDragToken = null;
@@ -7737,6 +7756,13 @@
mCurrentDragView = newDragTarget;
}
+ /** Sets the view that started drag and drop for the purpose of sending AccessibilityEvents */
+ void setDragStartedViewForAccessibility(View view) {
+ if (mStartedDragViewForA11y == null) {
+ mStartedDragViewForA11y = view;
+ }
+ }
+
private AudioManager getAudioManager() {
if (mView == null) {
throw new IllegalStateException("getAudioManager called when there is no mView");
@@ -7872,7 +7898,8 @@
return relayoutResult;
}
- private void updateOpacity(WindowManager.LayoutParams params, boolean dragResizing) {
+ private void updateOpacity(WindowManager.LayoutParams params, boolean dragResizing,
+ boolean forceUpdate) {
boolean opaque = false;
if (!PixelFormat.formatHasAlpha(params.format)
@@ -7888,7 +7915,7 @@
opaque = true;
}
- if (mIsSurfaceOpaque == opaque) {
+ if (!forceUpdate && mIsSurfaceOpaque == opaque) {
return;
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index c413a9b..ffb7efa 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -498,4 +498,9 @@
public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
RemoteCallback callback) {
}
+
+ @Override
+ public boolean dropForAccessibility(IWindow window, int x, int y) {
+ return false;
+ }
}
diff --git a/core/java/android/view/animation/TranslateAnimation.java b/core/java/android/view/animation/TranslateAnimation.java
index ec55a02..3365c70 100644
--- a/core/java/android/view/animation/TranslateAnimation.java
+++ b/core/java/android/view/animation/TranslateAnimation.java
@@ -57,6 +57,9 @@
/** @hide */
protected float mToYDelta;
+ private int mWidth;
+ private int mParentWidth;
+
/**
* Constructor used when a TranslateAnimation is loaded from a resource.
*
@@ -179,5 +182,60 @@
mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth);
mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight);
mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight);
+
+ mWidth = width;
+ mParentWidth = parentWidth;
+ }
+
+ /**
+ * Checks whether or not the translation is exclusively an x axis translation.
+ *
+ * @hide
+ */
+ public boolean isXAxisTransition() {
+ return mFromXDelta - mToXDelta != 0 && mFromYDelta - mToYDelta == 0;
+ }
+
+ /**
+ * Checks whether or not the translation is a full width x axis slide in or out translation.
+ *
+ * @hide
+ */
+ public boolean isFullWidthTranslate() {
+ boolean isXAxisSlideTransition =
+ isSlideInLeft() || isSlideOutRight() || isSlideInRight() || isSlideOutLeft();
+ return mWidth == mParentWidth && isXAxisSlideTransition;
+ }
+
+ private boolean isSlideInLeft() {
+ boolean startsOutOfParentOnLeft = mFromXDelta <= -mWidth;
+ return startsOutOfParentOnLeft && endsXEnclosedWithinParent();
+ }
+
+ private boolean isSlideOutRight() {
+ boolean endOutOfParentOnRight = mToXDelta >= mParentWidth;
+ return startsXEnclosedWithinParent() && endOutOfParentOnRight;
+ }
+
+ private boolean isSlideInRight() {
+ boolean startsOutOfParentOnRight = mFromXDelta >= mParentWidth;
+ return startsOutOfParentOnRight && endsXEnclosedWithinParent();
+ }
+
+ private boolean isSlideOutLeft() {
+ boolean endOutOfParentOnLeft = mToXDelta <= -mWidth;
+ return startsXEnclosedWithinParent() && endOutOfParentOnLeft;
+ }
+
+ private boolean endsXEnclosedWithinParent() {
+ return mWidth <= mParentWidth
+ && mToXDelta + mWidth <= mParentWidth
+ && mToXDelta >= 0;
+ }
+
+ private boolean startsXEnclosedWithinParent() {
+ return mWidth <= mParentWidth
+ && mFromXDelta + mWidth <= mParentWidth
+ && mFromXDelta >= 0;
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 42d77cd..e6f103e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2154,6 +2154,7 @@
* @hide
*/
public boolean requestImeShow(IBinder windowToken) {
+ checkFocus();
synchronized (mH) {
final View servedView = getServedViewLocked();
if (servedView == null || servedView.getWindowToken() != windowToken) {
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index eca7481..60402eb 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -593,9 +593,8 @@
final View rootView = roots.get(rootNum).getView();
if (rootView instanceof ViewGroup) {
findViewsTraversalByAutofillIds((ViewGroup) rootView, sourceViewIds);
- } else {
- addViewIfNeeded(sourceViewIds, rootView);
}
+ addViewIfNeeded(sourceViewIds, rootView);
}
}
@@ -606,9 +605,8 @@
final View child = viewGroup.getChildAt(i);
if (child instanceof ViewGroup) {
findViewsTraversalByAutofillIds((ViewGroup) child, sourceViewIds);
- } else {
- addViewIfNeeded(sourceViewIds, child);
}
+ addViewIfNeeded(sourceViewIds, child);
}
}
diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java
index a075662..66c028b 100644
--- a/core/java/android/view/translation/ViewTranslationCallback.java
+++ b/core/java/android/view/translation/ViewTranslationCallback.java
@@ -41,6 +41,11 @@
* method will not be called before {@link View#onViewTranslationResponse} or
* {@link View#onVirtualViewTranslationResponses}.
*
+ * <p> NOTE: It is possible the user changes text that causes a new
+ * {@link ViewTranslationResponse} returns to show the new translation. If you cache the
+ * {@link ViewTranslationResponse} here, you should remember to keep the cached value up
+ * to date.
+ *
* <p> NOTE: For TextView implementation, {@link ContentCaptureSession#notifyViewTextChanged}
* shouldn't be called with the translated text, simply calling setText() here will trigger the
* method. You should either override {@code View#onProvideContentCaptureStructure()} to report
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 9d60009..152405b 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -70,7 +70,12 @@
+ "onViewTranslationResponse().");
return false;
}
- if (mTranslationTransformation == null) {
+ // It is possible user changes text and new translation response returns, system should
+ // update the translation response to keep the result up to date.
+ // Because TextView.setTransformationMethod() will skip the same TransformationMethod
+ // instance, we should create a new one to let new translation can work.
+ if (mTranslationTransformation == null
+ || !response.equals(mTranslationTransformation.getViewTranslationResponse())) {
TransformationMethod originalTranslationMethod =
((TextView) view).getTransformationMethod();
mTranslationTransformation = new TranslationTransformationMethod(response,
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index c8a4425..9985262 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -272,4 +272,14 @@
void triggerHardwareRecognitionEventForTest(
in SoundTrigger.KeyphraseRecognitionEvent event,
in IHotwordRecognitionStatusCallback callback);
+
+ /**
+ * Starts to listen the status of visible activity.
+ */
+ void startListeningVisibleActivityChanged(in IBinder token);
+
+ /**
+ * Stops to listen the status of visible activity.
+ */
+ void stopListeningVisibleActivityChanged(in IBinder token);
}
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 84354d9..ec224e5 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -72,17 +72,19 @@
private Resources mSuspendingAppResources;
private SuspendDialogInfo mSuppliedDialogInfo;
private Bundle mOptions;
- private BroadcastReceiver mUnsuspendReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mSuspendModifiedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(intent.getAction())) {
- final String[] unsuspended = intent.getStringArrayExtra(
+ if (Intent.ACTION_PACKAGES_SUSPENSION_CHANGED.equals(intent.getAction())) {
+ // Suspension conditions were modified, dismiss any related visible dialogs.
+ final String[] modified = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
- if (ArrayUtils.contains(unsuspended, mSuspendedPackage)) {
+ if (ArrayUtils.contains(modified, mSuspendedPackage)) {
if (!isFinishing()) {
- Slog.w(TAG, "Package " + mSuspendedPackage
- + " got unsuspended while the dialog was visible. Finishing.");
+ Slog.w(TAG, "Package " + mSuspendedPackage + " has modified"
+ + " suspension conditions while dialog was visible. Finishing.");
SuspendedAppActivity.this.finish();
+ // TODO (b/198201994): reload the suspend dialog to show most relevant info
}
}
}
@@ -245,15 +247,16 @@
setupAlert();
- final IntentFilter unsuspendFilter = new IntentFilter(Intent.ACTION_PACKAGES_UNSUSPENDED);
- registerReceiverAsUser(mUnsuspendReceiver, UserHandle.of(mUserId), unsuspendFilter, null,
- null);
+ final IntentFilter suspendModifiedFilter =
+ new IntentFilter(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED);
+ registerReceiverAsUser(mSuspendModifiedReceiver, UserHandle.of(mUserId),
+ suspendModifiedFilter, null, null);
}
@Override
protected void onDestroy() {
super.onDestroy();
- unregisterReceiver(mUnsuspendReceiver);
+ unregisterReceiver(mSuspendModifiedReceiver);
}
private void requestDismissKeyguardIfNeeded(CharSequence dismissMessage) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7e286f0..a817119 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -8618,7 +8618,7 @@
* inactive so can be dropped.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public boolean reset(long uptimeUs, long realtimeUs) {
+ public boolean reset(long uptimeUs, long realtimeUs, int resetReason) {
boolean active = false;
mOnBatteryBackgroundTimeBase.init(uptimeUs, realtimeUs);
@@ -8688,7 +8688,11 @@
resetIfNotNull(mBluetoothControllerActivity, false, realtimeUs);
resetIfNotNull(mModemControllerActivity, false, realtimeUs);
- MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+ if (resetReason == RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE) {
+ mUidMeasuredEnergyStats = null;
+ } else {
+ MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+ }
resetIfNotNull(mUserCpuTime, false, realtimeUs);
resetIfNotNull(mSystemCpuTime, false, realtimeUs);
@@ -11371,7 +11375,7 @@
mNumConnectivityChange = 0;
for (int i=0; i<mUidStats.size(); i++) {
- if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs)) {
+ if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs, resetReason)) {
mUidStats.valueAt(i).detachFromTimeBase();
mUidStats.remove(mUidStats.keyAt(i));
i--;
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 0038579..980aec1 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -234,8 +235,9 @@
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
+ final String[] customEnergyConsumerNames = mStats.getCustomEnergyConsumerNames();
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- mStats.getCustomEnergyConsumerNames(), includePowerModels);
+ customEnergyConsumerNames, includePowerModels);
if (mBatteryUsageStatsStore == null) {
Log.e(TAG, "BatteryUsageStatsStore is unavailable");
return builder.build();
@@ -247,7 +249,14 @@
final BatteryUsageStats snapshot =
mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
if (snapshot != null) {
- builder.add(snapshot);
+ if (Arrays.equals(snapshot.getCustomPowerComponentNames(),
+ customEnergyConsumerNames)) {
+ builder.add(snapshot);
+ } else {
+ Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different "
+ + "custom power components: "
+ + Arrays.toString(snapshot.getCustomPowerComponentNames()));
+ }
}
}
}
diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
index 9b51a8e..bb307a0 100644
--- a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
+++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
@@ -21,13 +21,18 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
+import android.util.Slog;
import android.util.SparseArray;
+import java.util.Arrays;
+
/**
* Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type
* {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
*/
public class CustomMeasuredPowerCalculator extends PowerCalculator {
+ private static final String TAG = "CustomMeasuredPowerCalc";
+
public CustomMeasuredPowerCalculator(PowerProfile powerProfile) {
}
@@ -76,9 +81,9 @@
if (totalPowerMah == null) {
newTotalPowerMah = new double[customMeasuredPowerMah.length];
} else if (totalPowerMah.length != customMeasuredPowerMah.length) {
- newTotalPowerMah = new double[customMeasuredPowerMah.length];
- System.arraycopy(totalPowerMah, 0, newTotalPowerMah, 0,
- customMeasuredPowerMah.length);
+ Slog.wtf(TAG, "Number of custom energy components is not the same for all apps: "
+ + totalPowerMah.length + ", " + customMeasuredPowerMah.length);
+ newTotalPowerMah = Arrays.copyOf(totalPowerMah, customMeasuredPowerMah.length);
} else {
newTotalPowerMah = totalPowerMah;
}
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 776a705..3915b0e 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -215,6 +215,9 @@
+ "ms tx=" + txTime + "ms power=" + formatCharge(
powerDurationAndTraffic.powerMah));
}
+ } else {
+ powerDurationAndTraffic.durationMs = 0;
+ powerDurationAndTraffic.powerMah = 0;
}
} else {
final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 63d61fc..3f530fe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -55,6 +55,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
<protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" />
<protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENSION_CHANGED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY" />
<protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" />
<protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" />
diff --git a/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml
index 5add19b..aa38000 100644
--- a/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml
+++ b/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml
@@ -18,19 +18,9 @@
-->
<!-- This should be kept in sync with task_open_enter.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:hasRoundedCorners="true"
android:shareInterpolator="false"
- android:zAdjustment="top">
-
- <alpha
- android:fromAlpha="1"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="217"/>
+ android:zAdjustment="top"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="-105%"
@@ -38,36 +28,9 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0526"
- android:toXScale="1"
- android:fromYScale="1.0526"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
-
- <scale
- android:fromXScale="0.95"
- android:toXScale="1"
- android:fromYScale="0.95"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:startOffset="283"
- android:duration="317"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
<!-- To keep the thumbnail around longer and fade out the thumbnail -->
<alpha android:fromAlpha="1.0" android:toAlpha="0"
diff --git a/core/res/res/anim-ldrtl/task_close_enter.xml b/core/res/res/anim-ldrtl/task_close_enter.xml
index e00141a..5ace46d 100644
--- a/core/res/res/anim-ldrtl/task_close_enter.xml
+++ b/core/res/res/anim-ldrtl/task_close_enter.xml
@@ -14,20 +14,9 @@
~ limitations under the License
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="217"/>
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="105%"
@@ -35,34 +24,7 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0526"
- android:toXScale="1"
- android:fromYScale="1.0526"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
-
- <scale
- android:fromXScale="0.95"
- android:toXScale="1"
- android:fromYScale="0.95"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:startOffset="283"
- android:duration="317"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_close_exit.xml b/core/res/res/anim-ldrtl/task_close_exit.xml
index 71a44ae..76fbdff 100644
--- a/core/res/res/anim-ldrtl/task_close_exit.xml
+++ b/core/res/res/anim-ldrtl/task_close_exit.xml
@@ -15,19 +15,8 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="283"/>
+ android:shareInterpolator="false"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="0"
@@ -35,22 +24,9 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0"
- android:toXScale="0.95"
- android:fromYScale="1.0"
- android:toYScale="0.95"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:pivotX="50%"
- android:pivotY="50%"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
<!-- This is needed to keep the animation running while task_open_enter completes -->
<alpha
diff --git a/core/res/res/anim-ldrtl/task_open_enter.xml b/core/res/res/anim-ldrtl/task_open_enter.xml
index 7815f7d..52c74a6 100644
--- a/core/res/res/anim-ldrtl/task_open_enter.xml
+++ b/core/res/res/anim-ldrtl/task_open_enter.xml
@@ -16,20 +16,9 @@
<!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
<!-- This should in sync with cross_profile_apps_thumbnail_enter.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="217"/>
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="-105%"
@@ -37,34 +26,7 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0526"
- android:toXScale="1"
- android:fromYScale="1.0526"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
-
- <scale
- android:fromXScale="0.95"
- android:toXScale="1"
- android:fromYScale="0.95"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:startOffset="283"
- android:duration="317"/>
-</set>
\ No newline at end of file
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
+</set>
diff --git a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml
index 5fccd6df..90ec071 100644
--- a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml
+++ b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml
@@ -16,20 +16,9 @@
-->
<!-- This should in sync with task_open_enter.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="217"/>
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="-105%"
@@ -37,36 +26,9 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0526"
- android:toXScale="1"
- android:fromYScale="1.0526"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
-
- <scale
- android:fromXScale="0.95"
- android:toXScale="1"
- android:fromYScale="0.95"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:startOffset="283"
- android:duration="317"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
<!-- To keep the transition around longer for the thumbnail, should be kept in sync with
cross_profile_apps_thumbmail.xml -->
@@ -75,4 +37,4 @@
android:toAlpha="1.0"
android:startOffset="717"
android:duration="200"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/anim-ldrtl/task_open_exit.xml b/core/res/res/anim-ldrtl/task_open_exit.xml
index 025e1bd..beb6fca 100644
--- a/core/res/res/anim-ldrtl/task_open_exit.xml
+++ b/core/res/res/anim-ldrtl/task_open_exit.xml
@@ -15,19 +15,8 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="283"/>
+ android:shareInterpolator="false"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="0"
@@ -35,22 +24,9 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0"
- android:toXScale="0.95"
- android:fromYScale="1.0"
- android:toYScale="0.95"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:pivotX="50%"
- android:pivotY="50%"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
<!-- This is needed to keep the animation running while task_open_enter completes -->
<alpha
diff --git a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
index 2cfeecf..f6d7b72 100644
--- a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
+++ b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
@@ -19,18 +19,8 @@
<!-- This should be kept in sync with task_open_enter.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:hasRoundedCorners="true"
- android:zAdjustment="top">
-
- <alpha
- android:fromAlpha="1"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="217"/>
+ android:zAdjustment="top"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="105%"
@@ -38,36 +28,9 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0526"
- android:toXScale="1"
- android:fromYScale="1.0526"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
-
- <scale
- android:fromXScale="0.95"
- android:toXScale="1"
- android:fromYScale="0.95"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:startOffset="283"
- android:duration="317"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
<!-- To keep the thumbnail around longer and fade out the thumbnail -->
<alpha android:fromAlpha="1.0" android:toAlpha="0"
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index 487ff5c..52017b1 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -16,20 +16,9 @@
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="217"/>
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="-105%"
@@ -37,34 +26,7 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0526"
- android:toXScale="1"
- android:fromYScale="1.0526"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
-
- <scale
- android:fromXScale="0.95"
- android:toXScale="1"
- android:fromYScale="0.95"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:startOffset="283"
- android:duration="317"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index afc3256c..736f3f2 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -17,19 +17,8 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="283"/>
+ android:shareInterpolator="false"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="0"
@@ -37,22 +26,9 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0"
- android:toXScale="0.95"
- android:fromYScale="1.0"
- android:toYScale="0.95"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:pivotX="50%"
- android:pivotY="50%"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
<!-- This is needed to keep the animation running while task_open_enter completes -->
<alpha
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 0aafc1c..3c93438 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -15,23 +15,12 @@
** limitations under the License.
*/
-->
-<!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
-<!-- This should in sync with cross_profile_apps_thumbnail_enter.xml -->
+<!-- This should be kept in sync with task_open_enter_cross_profile_apps.xml -->
+<!-- This should be kept in sync with cross_profile_apps_thumbnail_enter.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="217"/>
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="105%"
@@ -39,34 +28,7 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0526"
- android:toXScale="1"
- android:fromYScale="1.0526"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
-
- <scale
- android:fromXScale="0.95"
- android:toXScale="1"
- android:fromYScale="0.95"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:startOffset="283"
- android:duration="317"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
index 702f7ba..16249d1 100644
--- a/core/res/res/anim/task_open_enter_cross_profile_apps.xml
+++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
@@ -18,20 +18,9 @@
-->
<!-- This should in sync with task_open_enter.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="217"/>
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="105%"
@@ -39,36 +28,9 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0526"
- android:toXScale="1"
- android:fromYScale="1.0526"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
-
- <scale
- android:fromXScale="0.95"
- android:toXScale="1"
- android:fromYScale="0.95"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:startOffset="283"
- android:duration="317"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
<!-- To keep the transition around longer for the thumbnail, should be kept in sync with
cross_profile_apps_thumbmail.xml -->
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 691317d..d170317 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -17,19 +17,8 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:hasRoundedCorners="true"
- android:showWallpaper="true">
-
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="67"
- android:duration="283"/>
+ android:shareInterpolator="false"
+ android:hasRoundedCorners="true">
<translate
android:fromXDelta="0"
@@ -37,22 +26,9 @@
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/aggressive_ease"
- android:startOffset="50"
- android:duration="383"/>
-
- <scale
- android:fromXScale="1.0"
- android:toXScale="0.95"
- android:fromYScale="1.0"
- android:toYScale="0.95"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:pivotX="50%"
- android:pivotY="50%"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="283"/>
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="500"/>
<!-- This is needed to keep the animation running while task_open_enter completes -->
<alpha
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/core/res/res/color/overview_background.xml
similarity index 67%
copy from packages/SystemUI/res/drawable/media_output_dialog_background.xml
copy to core/res/res/color/overview_background.xml
index 3ceb0f6..45c6c25 100644
--- a/packages/SystemUI/res/drawable/media_output_dialog_background.xml
+++ b/core/res/res/color/overview_background.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android">
- <shape android:shape="rectangle">
- <corners android:radius="8dp" />
- <solid android:color="?android:attr/colorBackground" />
- </shape>
-</inset>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_500" android:lStar="87" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/core/res/res/color/overview_background_dark.xml
similarity index 67%
rename from packages/SystemUI/res/drawable/media_output_dialog_background.xml
rename to core/res/res/color/overview_background_dark.xml
index 3ceb0f6..84f4fdf 100644
--- a/packages/SystemUI/res/drawable/media_output_dialog_background.xml
+++ b/core/res/res/color/overview_background_dark.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android">
- <shape android:shape="rectangle">
- <corners android:radius="8dp" />
- <solid android:color="?android:attr/colorBackground" />
- </shape>
-</inset>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 917a80c..8aafa0a 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -34,7 +34,7 @@
<string name="defaultMsisdnAlphaTag" msgid="2285034592902077488">"MSISDN1"</string>
<string name="mmiError" msgid="2862759606579822246">"সংযোগৰ সমস্যা বা MMI ক\'ড মান্য নহয়।"</string>
<string name="mmiFdnError" msgid="3975490266767565852">"কেৱল ফিক্সড ডায়েলিং নম্বৰৰ বাবে কার্য সীমাবদ্ধ কৰা হৈছে।"</string>
- <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"আপুনি ৰ\'মিঙত থকাৰ সময়ত কল ফৰৱাৰ্ডিঙৰ ছেটিংসমূহ সলনি কৰিব নোৱাৰি।"</string>
+ <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"আপুনি ৰ\'মিঙত থকাৰ সময়ত কল ফৰৱাৰ্ডিঙৰ ছেটিং সলনি কৰিব নোৱাৰি।"</string>
<string name="serviceEnabled" msgid="7549025003394765639">"সেৱা সক্ষম কৰা হ’ল।"</string>
<string name="serviceEnabledFor" msgid="1463104778656711613">"সেৱা সক্ষম কৰা হ’ল:"</string>
<string name="serviceDisabled" msgid="641878791205871379">"সেৱা অক্ষম কৰা হ’ল।"</string>
@@ -124,7 +124,7 @@
<string name="roamingTextSearching" msgid="5323235489657753486">"সেৱাৰ বাবে অনুসন্ধান কৰি থকা হৈছে"</string>
<string name="wfcRegErrorTitle" msgid="3193072971584858020">"ৱাই-ফাই কলিং ছেট আপ কৰিব পৰা নগ\'ল"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"ৱাই-ফাইৰ জৰিয়তে কল কৰিবলৈ আৰু বাৰ্তা পঠাবলৈ, প্ৰথমে আপোনাৰ বাহকক আপোনাৰ ডিভাইচটো ছেট আপ কৰিব দিবলৈ কওক। তাৰ পিচত, ছেটিংসমূহলৈ গৈ আকৌ ৱাই-ফাই কলিং অন কৰক। (ত্ৰুটি ক\'ড: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"ৱাই-ফাইৰ জৰিয়তে কল কৰিবলৈ আৰু বাৰ্তা পঠাবলৈ, প্ৰথমে আপোনাৰ বাহকক আপোনাৰ ডিভাইচটো ছেট আপ কৰিবলৈ কওক। তাৰ পাছত, ছেটিঙলৈ গৈ আকৌ ৱাই-ফাই কলিং অন কৰক। (ত্ৰুটি ক\'ড: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4795145070505729156">"আপোনাৰ বাহকৰ ওচৰত ৱাই-ফাই কলিং সুবিধা পঞ্জীয়ন কৰাত সমস্যাৰ উদ্ভৱ হৈছে: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -218,7 +218,7 @@
<string name="silent_mode" msgid="8796112363642579333">"নিঃশব্দ ম\'ড"</string>
<string name="turn_on_radio" msgid="2961717788170634233">"ৱায়াৰলেছ অন কৰক"</string>
<string name="turn_off_radio" msgid="7222573978109933360">"ৱায়াৰলেছ অফ কৰক"</string>
- <string name="screen_lock" msgid="2072642720826409809">"স্ক্ৰীণ লক"</string>
+ <string name="screen_lock" msgid="2072642720826409809">"স্ক্ৰীন লক"</string>
<string name="power_off" msgid="4111692782492232778">"পাৱাৰ অফ"</string>
<string name="silent_mode_silent" msgid="5079789070221150912">"ৰিংগাৰ অফ আছে"</string>
<string name="silent_mode_vibrate" msgid="8821830448369552678">"ৰিংগাৰ কম্পন অৱস্থাত আছে"</string>
@@ -268,7 +268,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"এয়াৰপ্লেইন ম\'ড"</string>
<string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"এয়াৰপ্লেইন ম\'ড অন কৰা আছে"</string>
<string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"এয়াৰপ্লেইন ম\'ড অফ কৰা আছে"</string>
- <string name="global_action_settings" msgid="4671878836947494217">"ছেটিংসমূহ"</string>
+ <string name="global_action_settings" msgid="4671878836947494217">"ছেটিং"</string>
<string name="global_action_assist" msgid="2517047220311505805">"সহায়"</string>
<string name="global_action_voice_assist" msgid="6655788068555086695">"কণ্ঠধ্বনিৰে সহায়"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"লকডাউন"</string>
@@ -400,8 +400,8 @@
<string name="permdesc_foregroundService" msgid="8720071450020922795">"এপটোক অগ্ৰভূমি সেৱাসমূহ ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"এপৰ সঞ্চয়াগাৰৰ খালী ঠাই হিচাপ কৰক"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"এপটোক ইয়াৰ ক\'ড, ডেটা আৰু কেশ্বৰ আকাৰ বিচাৰি উলিয়াবলৈ অনুমতি দিয়ে"</string>
- <string name="permlab_writeSettings" msgid="8057285063719277394">"ছিষ্টেম ছেটিংসমূহ সংশোধন কৰক"</string>
- <string name="permdesc_writeSettings" msgid="8293047411196067188">"এপটোক ছিষ্টেমৰ ছেটিংসমূহৰ ডেটা সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ ক্ষতিকাৰক এপসমূহে আপোনাৰ ছিষ্টেম কনফিগাৰেশ্বনক ক্ষতিগ্ৰস্ত কৰিব পাৰে৷"</string>
+ <string name="permlab_writeSettings" msgid="8057285063719277394">"ছিষ্টেম ছেটিংহ সংশোধন কৰক"</string>
+ <string name="permdesc_writeSettings" msgid="8293047411196067188">"এপ্টোক ছিষ্টেমৰ ছেটিঙৰ ডেটা সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ ক্ষতিকাৰক এপ্সমূহে আপোনাৰ ছিষ্টেম কনফিগাৰেশ্বনক ক্ষতিগ্ৰস্ত কৰিব পাৰে৷"</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"আৰম্ভ হোৱাৰ সময়ত চলাওক"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"ছিষ্টেমে বুট কৰা কাৰ্য সমাপ্ত কৰাৰ লগে লগে এপটোক নিজে নিজে আৰম্ভ হ\'বলৈ অনুমতি দিয়ে। ইয়াৰ ফলত ফ\'নটো ষ্টাৰ্ট হওতে বেছি সময়ৰ প্ৰয়োজন হ\'ব পাৰে, আৰু এপটো সদায় চলি থকাৰ কাৰণে ফ\'নটো সামগ্ৰিকভাৱে লেহেমীয়া হ\'ব পাৰে।"</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"ছিষ্টেমে বুটিং সমাপ্ত কৰাৰ লগে লগে এই এপ্টোক নিজে নিজে আৰম্ভ হ’বলৈ অনুমতি দিয়ে। এই কাৰ্যৰ বাবে আপোনাৰ Android TV ডিভাইচটো আৰম্ভ হ’বলৈ দীঘলীয়া সময়ৰ প্ৰয়োজন হ’ব পাৰে আৰু সকলো সময়তে চলি থাকি এপ্টোক সামগ্ৰিকভাৱে ডিভাইচটো লেহেমীয়া কৰিবলৈ দিয়ে।"</string>
@@ -442,8 +442,8 @@
<string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"এই এপ্টো ব্যৱহাৰ হৈ থকা অৱস্থাত ই অৱস্থান সেৱাসমূহৰ পৰা আপোনাৰ আনুমানিক অৱস্থান লাভ কৰিব পাৰে। এপ্টোৱে অৱস্থান লাভ কৰিবলৈ হ’লে আপোনাৰ ডিভাইচৰ অৱস্থান সেৱাসমূহ অন কৰি ৰাখিবই লাগিব।"</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"নেপথ্যত চলি থকা সময়ত অৱস্থানৰ এক্সেছ"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"এই এপ্টোৱে যিকোনো সময়তে অৱস্থান এক্সেছ কৰিব পাৰে, আনকি এপ্টো ব্যৱহাৰ হৈ নথকা অৱস্থাতো।"</string>
- <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"আপোনাৰ অডিঅ\' ছেটিংসমূহ সলনি কৰক"</string>
- <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"এপটোক ভলিউমৰ দৰে গ্ল\'বেল অডিঅ\' ছেটিংসমূহ যাৰ স্পীকাৰক আউটপুটৰ বাবে ব্যৱহাৰ হয় তাক সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
+ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"আপোনাৰ অডিঅ\' ছেটিং সলনি কৰক"</string>
+ <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"এপ্টোক ভলিউমৰ দৰে গ্ল\'বেল অডিঅ\' ছেটিং আৰু আউটপুটৰ বাবে কোনটো স্পীকাৰ ব্যৱহাৰ হয় তাক সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
<string name="permlab_recordAudio" msgid="1208457423054219147">"অডিঅ\' ৰেকর্ড কৰক"</string>
<string name="permdesc_recordAudio" msgid="5857246765327514062">"এই এপ্টোৱে ইয়াক ব্যৱহাৰ কৰি থাকোঁতে মাইক্ৰ’ফ’ন ব্যৱহাৰ কৰি অডিঅ’ ৰেকর্ড কৰিব পাৰে।"</string>
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"নেপথ্যত অডিঅ’ ৰেকৰ্ড কৰক"</string>
@@ -519,7 +519,7 @@
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"আপোনাৰ টেবলেটৰ লগতে মাল্টিকাষ্ট ঠিকনাবোৰ ও ব্যৱহাৰ কৰি এপক ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেট প্ৰাপ্ত কৰিবলৈ অনুমতি দিয়ে। এই কার্যই ন\'ন মাল্টিকাষ্ট ম\'ডতকৈ বেটাৰিৰ অধিক চ্চাৰ্জ ব্যৱহাৰ কৰে।"</string>
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"কেৱল আপোনাৰ Android TV ডিভাইচটোৱেই নহয়, মাল্টিকাষ্ট ঠিকনাবোৰ ব্যৱহাৰ কৰি এটা ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেটবোৰ লাভ কৰিবলৈ এপ্টোক অনুমতি দিয়ে। এই কার্যই ন’ন-মাল্টিকাষ্ট ম’ডতকৈ অধিক পাৱাৰ ব্যৱহাৰ কৰে।"</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"আপোনাৰ ফ\'নৰ লগতে মাল্টিকাষ্ট ঠিকনাবোৰ ও ব্যৱহাৰ কৰি এপক ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেট প্ৰাপ্ত কৰিবলৈ অনুমতি দিয়ে। এই কার্যই ন\'ন মাল্টিকাষ্ট ম\'ডতকৈ বেটাৰিৰ অধিক চ্চাৰ্জ ব্যৱহাৰ কৰে।"</string>
- <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ব্লুটুথ ছেটিংসমূহ ব্যৱহাৰ কৰক"</string>
+ <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ব্লুটুথ ছেটিং এক্সেছ কৰক"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"স্থানীয় ব্লুটুথ টে\'বলেট কনফিগাৰ কৰিবলৈ আৰু দূৰৱৰ্তী ডিভাইচসমূহৰ সৈতে যোৰা লগাবলৈ আৰু বিচাৰি উলিয়াবলৈ এপটোক অনুমতি দিয়ে।"</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"এপ্টোক আপোনাৰ Android TV ডিভাইচটোত ব্লুটুথ কনফিগাৰ কৰিবলৈ আৰু ৰিম’ট ডিভাইচসমূহ বিচাৰি উলিয়াবলৈ আৰু পেয়াৰ কৰিবলৈ অনুমতি দিয়ে।"</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"স্থানীয় ব্লুটুথ ফ\'ন কনফিগাৰ কৰিবলৈ আৰু দূৰৱৰ্তী ডিভাইচসমূহৰ সৈতে যোৰা লগাবলৈ আৰু বিচাৰি উলিয়াবলৈ এপটোক অনুমতি দিয়ে।"</string>
@@ -547,7 +547,7 @@
<string name="permdesc_nfc" msgid="8352737680695296741">"এপটোক নিয়েৰ ফিল্ড কমিউনিকেশ্বন (NFC) টেগ, কাৰ্ড আৰু ৰিডাৰসমূহৰ সৈতে যোগাযোগ কৰিবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_disableKeyguard" msgid="3605253559020928505">"আপোনাৰ স্ক্ৰীন লক অক্ষম কৰক"</string>
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণস্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰ পিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷"</string>
- <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"স্ক্ৰীণ লকৰ জটিলতাৰ অনুৰোধ"</string>
+ <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"স্ক্ৰীন লকৰ জটিলতাৰ অনুৰোধ"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"এপ্টোক স্ক্ৰীন লকৰ জটিলতাৰ স্তৰ (উচ্চ, মধ্যম, নিম্ন বা একেবাৰে নাই)ৰ বিষয়ে জানিবলৈ অনুমতি দিয়ে, যিয়ে স্ক্ৰীন লকৰ সম্ভাব্য দৈৰ্ঘ্য বা স্ক্ৰীন লকৰ প্ৰকাৰ দৰ্শায়। লগতে এপ্টোৱে ব্যৱহাৰকাৰীক স্ক্ৰীন লকটো এটা নিৰ্দিষ্ট স্তৰলৈ আপডে’ট কৰিবলৈ পৰামৰ্শ দিব পাৰে যিটো ব্যৱহাৰকাৰীয়ে অৱজ্ঞা কৰি পৰৱর্তী পৃষ্ঠালৈ যাব পাৰে। মনত ৰাখিব যে স্ক্ৰীন লকটো সাধাৰণ পাঠ হিচাপে ষ্ট\'ৰ কৰা নহয়; সেয়েহে, এপ্টোৱে সঠিক পাছৱৰ্ডটো জানিব নোৱাৰে।"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰক"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে"</string>
@@ -640,7 +640,7 @@
<string name="face_acquired_tilt_too_extreme" msgid="8618210742620248049">"আপোনাৰ মূৰটো অলপ কমকৈ হেলনীয়া কৰক।"</string>
<string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"আপোনাৰ মূৰটো সামান্য কমকৈ ঘূৰাওক।"</string>
<string name="face_acquired_obscured" msgid="4917643294953326639">"আপোনাৰ মুখখন ঢাকি ৰখা বস্তুবোৰ আঁতৰাওক।"</string>
- <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ক’লা বাৰডালকে ধৰি আপোনাৰ স্ক্রীণৰ ওপৰৰ অংশ চাফা কৰক"</string>
+ <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ক’লা বাৰডালকে ধৰি আপোনাৰ স্ক্রীনৰ ওপৰৰ অংশ চাফা কৰক"</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"মুখমণ্ডল সত্যাপন কৰিব পৰা নগ’ল। হাৰ্ডৱেৰ নাই।"</string>
@@ -664,8 +664,8 @@
</string-array>
<string name="face_error_vendor_unknown" msgid="7387005932083302070">"কিবা ভুল হ’ল। পুনৰ চেষ্টা কৰক।"</string>
<string name="face_icon_content_description" msgid="465030547475916280">"মুখমণ্ডলৰ আইকন"</string>
- <string name="permlab_readSyncSettings" msgid="6250532864893156277">"ছিংকৰ ছেটিংসমূহ পঢ়ক"</string>
- <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ কোনো একাউণ্টত ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।"</string>
+ <string name="permlab_readSyncSettings" msgid="6250532864893156277">"ছিংকৰ ছেটিং পঢ়ক"</string>
+ <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ্টো কোনো একাউণ্টৰ সৈতে ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।"</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"ছিংকক অন আৰু অফ ট\'গল কৰক"</string>
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"এপটোক কোনো একাউণ্টৰ ছিংক সম্পৰ্কীয় ছেটিংসমূহ সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ উদাহৰণস্বৰূপে, এই কাৰ্যক কোনো একাউণ্টৰ জৰিয়তে People এপটোৰ ছিংক সক্ষম কৰিবলৈ ব্যৱহাৰ কৰিব পাৰি৷"</string>
<string name="permlab_readSyncStats" msgid="3747407238320105332">"ছিংকৰ পৰিসংখ্যা পঢ়ক"</string>
@@ -727,9 +727,9 @@
<string name="policylab_limitPassword" msgid="4851829918814422199">"পাছৱর্ডৰ নিয়ম ছেট কৰক"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"স্ক্ৰীন লক পাছৱৰ্ড আৰু পিনত অনুমোদিত দৈৰ্ঘ্য আৰু বৰ্ণবোৰ নিয়ন্ত্ৰণ কৰক।।"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্ৰীন আনলক কৰা প্ৰয়াসবোৰ নিৰীক্ষণ কৰক"</string>
- <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"স্ক্ৰীণ আনলক কৰোতে লিখা অশুদ্ধ পাছৱৰ্ডৰ হিচাপ ৰাখক, আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড লিখা হয় তেন্তে টে\'বলেটটো লক কৰক বা টে\'বলেটটোৰ সকলো ডেটা মোহাৰক।"</string>
- <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"স্ক্ৰীণ আনলক কৰোঁতে দিয়া ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড দিয়া হয় তেন্তে Android TV ডিভাইচটো লক কৰক অথবা আপোনাৰ Android TV ডিভাইচৰ সকলো ডেটা মচক।"</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"স্ক্ৰীণ আনলক কৰোতে লিখা অশুদ্ধ পাছৱৰ্ডৰ হিচাপ ৰাখক, আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড লিখা হয় তেন্তে ফ\'নটো লক কৰক বা ফ\'নটোৰ সকলো ডেটা মোহাৰক।"</string>
+ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে টেবলেটটো লক কৰক বা টেবলেটটোৰ আটাইখিনি ডেটা মচক।"</string>
+ <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে Android TV ডিভাইচটো লক কৰক অথবা আপোনাৰ Android TV ডিভাইচৰ আটাইখিনি ডেটা মচক।"</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে ফ\'নটো লক কৰক বা ফ\'নটোৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে টেবলেটটো লক কৰক বা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"স্ক্ৰীনখন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে Android TV ডিভাইচটো লক কৰক অথবা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"স্ক্ৰীনখন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে ফ\'নটো লক কৰক অথবা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
@@ -754,7 +754,7 @@
<string name="policylab_disableCamera" msgid="5749486347810162018">"কেমেৰাবোৰ অক্ষম কৰক"</string>
<string name="policydesc_disableCamera" msgid="3204405908799676104">"সকলো ডিভাইচৰ কেমেৰাবোৰ ব্যৱহাৰ কৰাত বাধা দিয়ক।"</string>
<string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"স্ক্ৰীন লকৰ কিছুমান সুবিধা অক্ষম কৰক"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"স্ক্ৰীণ লকৰ কিছুমান সুবিধা ব্যৱহাৰ হোৱাত বাধা দিয়ক।"</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"স্ক্ৰীন লকৰ কিছুমান সুবিধা ব্যৱহাৰ হোৱাত বাধা দিয়ক।"</string>
<string-array name="phoneTypes">
<item msgid="8996339953292723951">"ঘৰ"</item>
<item msgid="7740243458912727194">"ম’বাইল"</item>
@@ -1212,7 +1212,7 @@
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"প্ৰতিচ্ছবি তোলক"</string>
<string name="alwaysUse" msgid="3153558199076112903">"এই কার্যৰ বাবে পূর্বনির্ধাৰিত ধৰণে ব্যৱহাৰ কৰক।"</string>
<string name="use_a_different_app" msgid="4987790276170972776">"এটা পৃথক এপ্ ব্যৱহাৰ কৰক"</string>
- <string name="clearDefaultHintMsg" msgid="1325866337702524936">"ছিষ্টেমৰ ছেটিংসমূহ > এপসমূহ > ডাউনল’ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷"</string>
+ <string name="clearDefaultHintMsg" msgid="1325866337702524936">"ছিষ্টেমৰ ছেটিং > এপ্ > ডাউনল’ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷"</string>
<string name="chooseActivity" msgid="8563390197659779956">"কোনো কার্য বাছনি কৰক"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"ইউএছবি ডিভাইচৰ বাবে এটা এপ্ বাছনি কৰক"</string>
<string name="noApplications" msgid="1186909265235544019">"কোনো এপে এই কাৰ্য কৰিব নোৱাৰে।"</string>
@@ -1240,7 +1240,7 @@
<string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক পূৰ্বতে লঞ্চ কৰা হৈছিল৷"</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"স্কেল"</string>
<string name="screen_compat_mode_show" msgid="5080361367584709857">"সদায় দেখুৱাওক"</string>
- <string name="screen_compat_mode_hint" msgid="4032272159093750908">"ছিষ্টেমৰ ছেটিংসমূহ > এপসমূহ > ডাউনল’ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।"</string>
+ <string name="screen_compat_mode_hint" msgid="4032272159093750908">"ছিষ্টেমৰ ছেটিং > এপ্ > ডাউনল’ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।"</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বর্তমানৰ ডিছপ্লে’ৰ আকাৰ ছেটিং ব্যৱহাৰ কৰিব নোৱাৰে আৰু ই সঠিকভাৱে নচলিবও পাৰে।"</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"সদায় দেখুৱাওক"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক এটা খাপ নোখোৱা Android OS সংস্কৰণৰ বাবে তৈয়াৰ কৰা হৈছিল, যাৰ ফলত ই অস্বাভাৱিকধৰণে আচৰণ কৰিব পাৰে। এপটোৰ শেহতীয়া সংস্কৰণ উপলব্ধ হ\'ব পাৰে।"</string>
@@ -1337,7 +1337,7 @@
<string name="sms_short_code_confirm_allow" msgid="920477594325526691">"পঠিয়াওক"</string>
<string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"বাতিল কৰক"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"মোৰ পচন্দ মনত ৰাখিব"</string>
- <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"আপুনি ইয়াক পিছত ছেটিং > এপ্-ত সলনি কৰিব পাৰে"</string>
+ <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"আপুনি ইয়াক পিছত ছেটিং > এপত সলনি কৰিব পাৰে"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"যিকোনো সময়ত অনুমতি দিয়ক"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"কেতিয়াও অনুমতি নিদিব"</string>
<string name="sim_removed_title" msgid="5387212933992546283">"ছিম কাৰ্ড আঁতৰোৱা হ’ল"</string>
@@ -1402,7 +1402,7 @@
<string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"অন্য এপৰ ওপৰত দেখুৱায়"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> অন্য এপসমূহৰ ওপৰত প্ৰদৰ্শিত হৈ আছে"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g>এ অইন এপবোৰৰ ওপৰত প্ৰদৰ্শিত হৈ আছে"</string>
- <string name="alert_windows_notification_message" msgid="6538171456970725333">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g>এ এই সুবিধাটো ব্যৱহাৰ কৰাটো নিবিচাৰে তেন্তে টিপি ছেটিংসমূহ খোলক আৰু ইয়াক অফ কৰক।"</string>
+ <string name="alert_windows_notification_message" msgid="6538171456970725333">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g>এ এই সুবিধাটো ব্যৱহাৰ কৰাটো নিবিচাৰে তেন্তে ছেটিং খুলিবলৈ টিপক আৰু ইয়াক অফ কৰক।"</string>
<string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"অফ কৰক"</string>
<string name="ext_media_checking_notification_title" msgid="8299199995416510094">"<xliff:g id="NAME">%s</xliff:g> পৰীক্ষা কৰি থকা হৈছে…"</string>
<string name="ext_media_checking_notification_message" msgid="2231566971425375542">"বৰ্তমানৰ সমলৰ সমীক্ষা কৰি থকা হৈছে"</string>
@@ -1504,7 +1504,7 @@
<string name="vpn_lockdown_connected" msgid="2853127976590658469">"সদা-সক্ৰিয় ভিপিএন সংযোগ কৰা হ’ল"</string>
<string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ বিচ্ছিন্ন কৰা হৈছে"</string>
<string name="vpn_lockdown_error" msgid="4453048646854247947">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ কৰিব পৰা নাই"</string>
- <string name="vpn_lockdown_config" msgid="8331697329868252169">"নেটৱৰ্ক বা ভিপিএন ছেটিংসমূহ সলনি কৰক"</string>
+ <string name="vpn_lockdown_config" msgid="8331697329868252169">"নেটৱৰ্ক বা VPN ছেটিং সলনি কৰক"</string>
<string name="upload_file" msgid="8651942222301634271">"ফাইল বাছনি কৰক"</string>
<string name="no_file_chosen" msgid="4146295695162318057">"কোনো ফাইল বাছনি কৰা হোৱা নাই"</string>
<string name="reset" msgid="3865826612628171429">"ৰিছেট কৰক"</string>
@@ -1692,10 +1692,10 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাট ব্যৱহাৰ কৰেনে?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"শ্বৰ্টকাটটো অন হৈ থকাৰ সময়ত দুয়োটা ভলিউম বুটাম ৩ ছেকেণ্ডৰ বাবে হেঁচি ধৰি ৰাখিলে এটা সাধ্য সুবিধা আৰম্ভ হ’ব।"</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"সাধ্য সুবিধাসমূহৰ বাবে শ্বৰ্টকাট অন কৰিবনে?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাত কিছুমান নিৰ্দিষ্ট সুবিধা সলনি কৰিব পাৰে।"</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nআপুনি ছেটিং > সাধ্য-সুবিধাত কিছুমান নিৰ্দিষ্ট সুবিধা সলনি কৰিব পাৰে।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g>ৰ শ্বৰ্টকাট অন কৰিবনে?"</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।"</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিং > সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"অন কৰক"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"অন নকৰিব"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"অন কৰা আছে"</string>
@@ -1703,8 +1703,8 @@
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g>ক আপোনাৰ ডিভাইচটোৰ সম্পূর্ণ নিয়ন্ত্ৰণ দিবনে?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"যদি আপুনি <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে, তেন্তে আপোনাৰ ডিভাইচটোৱে ডেটা এনক্ৰিপশ্বনৰ গুণগত মান উন্নত কৰিবলৈ স্ক্ৰীন লক ব্যৱহাৰ নকৰে।"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"আপোনাক সাধ্য সুবিধাৰ প্ৰয়োজনসমূহৰ জৰিয়তে সহায় কৰা এপ্সমূহৰ বাবে সম্পূর্ণ নিয়ন্ত্ৰণৰ সুবিধাটো সঠিক যদিও অধিকাংশ এপৰ বাবে এয়া সঠিক নহয়।"</string>
- <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"চাওক আৰু স্ক্ৰীণ নিয়ন্ত্ৰণ কৰক"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ই স্ক্ৰীণৰ সকলো সমল পঢ়িব পাৰে আৰু অন্য এপ্সমূহৰ ওপৰত সমল প্ৰদর্শন কৰিব পাৰে।"</string>
+ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"চাওক আৰু স্ক্ৰীন নিয়ন্ত্ৰণ কৰক"</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ই স্ক্ৰীনত থকা আটাইখিনি সমল পঢ়িব পাৰে আৰু অন্য এপ্সমূহৰ ওপৰত সমল প্ৰদর্শন কৰিব পাৰে।"</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"কার্যসমূহ চাওক আৰু কৰক"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ই আপুনি কোনো এপ্ বা হার্ডৱেৰ ছেন্সৰৰ সৈতে কৰা ভাব-বিনিময় আৰু আপোনাৰ হৈ অন্য কোনো লোকে এপৰ সৈতে কৰা ভাব-বিনিময় ট্ৰেক কৰিব পাৰে।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"অনুমতি দিয়ক"</string>
@@ -1854,7 +1854,7 @@
<item quantity="other"> <xliff:g id="COUNT">%d</xliff:g> ছেকেণ্ডত আকৌ চেষ্টা কৰক</item>
</plurals>
<string name="restr_pin_try_later" msgid="5897719962541636727">"পিছত আকৌ চেষ্টা কৰক"</string>
- <string name="immersive_cling_title" msgid="2307034298721541791">"স্ক্ৰীণ পূৰ্ণৰূপত চাই আছে"</string>
+ <string name="immersive_cling_title" msgid="2307034298721541791">"স্ক্ৰীন পূৰ্ণৰূপত চাই আছে"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"বাহিৰ হ\'বলৈ ওপৰৰপৰা তললৈ ছোৱাইপ কৰক।"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"বুজি পালোঁ"</string>
<string name="done_label" msgid="7283767013231718521">"সম্পন্ন কৰা হ’ল"</string>
@@ -2087,7 +2087,7 @@
<string name="zen_upgrade_notification_title" msgid="8198167698095298717">"অসুবিধা নিদিব সলনি হৈছে"</string>
<string name="zen_upgrade_notification_content" msgid="5228458567180124005">"কি কি অৱৰোধ কৰা হৈছে জানিবলৈ টিপক।"</string>
<string name="notification_app_name_system" msgid="3045196791746735601">"ছিষ্টেম"</string>
- <string name="notification_app_name_settings" msgid="9088548800899952531">"ছেটিংসমূহ"</string>
+ <string name="notification_app_name_settings" msgid="9088548800899952531">"ছেটিং"</string>
<string name="notification_appops_camera_active" msgid="8177643089272352083">"কেমেৰা"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"মাইক্ৰ\'ফ\'ন"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ আছে"</string>
@@ -2141,7 +2141,7 @@
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"উভতি যাওক"</string>
<string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"শেহতীয়া এপ্সমূহ"</string>
<string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"জাননীসমূহ"</string>
- <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"ক্ষিপ্ৰ ছেটিংসমূহ"</string>
+ <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"ক্ষিপ্ৰ ছেটিং"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"পাৱাৰ ডায়লগ"</string>
<string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"লক স্ক্ৰীন"</string>
<string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"স্ক্ৰীণশ্বট"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index a35c39e..7bc5905 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -325,7 +325,7 @@
<string name="permgrouplab_phone" msgid="570318944091926620">"Telèfon"</string>
<string name="permgroupdesc_phone" msgid="270048070781478204">"fer i gestionar trucades telefòniques"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensors corporals"</string>
- <string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedir a les dades del sensor sobre els signes vitals"</string>
+ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedir a les dades del sensor sobre les constants vitals"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contingut de la finestra"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contingut d\'una finestra amb què estàs interaccionant."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar Exploració tàctil"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c653c31..afc0644 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1058,7 +1058,7 @@
</plurals>
<string name="last_month" msgid="1528906781083518683">"El mes pasado"</string>
<string name="older" msgid="1645159827884647400">"Anterior"</string>
- <string name="preposition_for_date" msgid="2780767868832729599">"el <xliff:g id="DATE">%s</xliff:g>"</string>
+ <string name="preposition_for_date" msgid="2780767868832729599">"<xliff:g id="DATE">%s</xliff:g>"</string>
<string name="preposition_for_time" msgid="4336835286453822053">"a las <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="preposition_for_year" msgid="3149809685340130039">"en <xliff:g id="YEAR">%s</xliff:g>"</string>
<string name="day" msgid="8394717255950176156">"día"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 221c20e..d674a5d9 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -601,7 +601,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossible utiliser capteur empreinte digitale. Consultez un fournisseur de services de réparation"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossible d\'utiliser le capteur d\'empreintes digitales. Consultez un fournisseur de services de réparation"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser l\'empreinte digitale ou le verrouillage de l\'écran"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 304bd15..6cde847 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -747,10 +747,10 @@
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"इस फ़ोन पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"डिवाइस वैश्विक प्रॉक्सी सेट करें"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"नीति चालू होने के दौरान इस्तेमाल करने के लिए डिवाइस ग्लोबल प्रॉक्सी सेट करें. केवल डिवाइस का मालिक ही ग्लोबल प्रॉक्सी सेट कर सकता है."</string>
- <string name="policylab_expirePassword" msgid="6015404400532459169">"स्क्रीन लॉक पासवर्ड समाप्ति सेट करें"</string>
+ <string name="policylab_expirePassword" msgid="6015404400532459169">"स्क्रीन लॉक पासवर्ड के खत्म होने की अवधि सेट करें"</string>
<string name="policydesc_expirePassword" msgid="9136524319325960675">"यह बदलें कि स्क्रीन लॉक पासवर्ड, पिन या पैटर्न को कितने समय में बदला जाना चाहिए."</string>
<string name="policylab_encryptedStorage" msgid="9012936958126670110">"मेमोरी को सुरक्षित करने का तरीका सेट करें"</string>
- <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"संग्रहित ऐप्स डेटा को एन्क्रिप्ट किया जाना आवश्यक है."</string>
+ <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"स्टोर किए गए ऐप डेटा को एन्क्रिप्ट किया जाना ज़रूरी है."</string>
<string name="policylab_disableCamera" msgid="5749486347810162018">"कैमरों को अक्षम करें"</string>
<string name="policydesc_disableCamera" msgid="3204405908799676104">"सभी डिवाइस कैमरों का उपयोग रोकें."</string>
<string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"स्क्रीन लॉक की कुछ सुविधाएं बंद करना"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 62639a3..9498980 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -916,7 +916,7 @@
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Құпия сөзді <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін қайталаңыз."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"PIN кодын <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін қайталаңыз."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін сізден Google жүйесіне кіріп планшет бекітпесін ашу сұралады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс сызбадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың құлпын ашу үшін Google есептік жазбаңызға кіру керек болады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталап көріңіз."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс сызбадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың құлпын ашу үшін Google аккаунтыңызға кіру керек болады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталап көріңіз."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін сізден Google жүйесіне кіріп телефон бекітпесін ашу сұралады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Планшеттің бекітпесін ашуға <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін, телефон зауыттың бастапқы параметрлеріне қайта реттеледі және пайдаланушы деректері жоғалады."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Android TV құрылғыңыздың құлпын <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс ашпадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың зауыттық әдепкі параметрлері қайтарылады және одан барлық пайдаланушы деректері өшіп қалады."</string>
@@ -928,7 +928,7 @@
<string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Кескінді ұмытып қалдыңыз ба?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Аккаунттың бекітпесін ашу"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Тым көп кескін әрекеттері"</string>
- <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ашу үшін Google есептік жазбаңызбен кіріңіз."</string>
+ <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ашу үшін Google аккаунтыңызбен кіріңіз."</string>
<string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Пайдаланушы атауы (эл. пошта)"</string>
<string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Құпия сөз"</string>
<string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Кіру"</string>
@@ -1332,8 +1332,8 @@
<string name="sms_control_yes" msgid="4858845109269524622">"Рұқсат беру"</string>
<string name="sms_control_no" msgid="4845717880040355570">"Өшіру"</string>
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасы <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> мекенжайына хабар жіберуді қалайды."</string>
- <string name="sms_short_code_details" msgid="2723725738333388351">"Бұл мобильді есептік жазбаңызда "<b>"өзгерістер"</b>" тудыруы мүмкін."</string>
- <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Бұл мобильді есептік жазбаңызда өзгерістерді тудырады."</b></string>
+ <string name="sms_short_code_details" msgid="2723725738333388351">"Бұл мобильді аккаунтыңызда "<b>"өзгерістер"</b>" тудыруы мүмкін."</string>
+ <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Бұл мобильді аккаунтыңызда өзгерістерді тудырады."</b></string>
<string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Жіберу"</string>
<string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Бас тарту"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Менің таңдауым есте сақталсын"</string>
@@ -1477,7 +1477,7 @@
<string name="ime_action_default" msgid="8265027027659800121">"Орындау"</string>
<string name="dial_number_using" msgid="6060769078933953531">"Нөмірді\n <xliff:g id="NUMBER">%s</xliff:g> қолданып теріңіз"</string>
<string name="create_contact_using" msgid="6200708808003692594">"Байланысты\n <xliff:g id="NUMBER">%s</xliff:g> нөмірі арқылы орнату"</string>
- <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Келесі бір немесе бірнеше қолданба қазір және болашақта есептік жазбаңызға қатынасуға рұқсатты сұрап жатыр."</string>
+ <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Келесі бір немесе бірнеше қолданба қазір және болашақта аккаунтыңызға қатынасуға рұқсатты сұрап жатыр."</string>
<string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Бұл өтініштің орындалуын қалайсыз ба?"</string>
<string name="grant_permissions_header_text" msgid="3420736827804657201">"Кіру өтініші"</string>
<string name="allow" msgid="6195617008611933762">"Рұқсат беру"</string>
@@ -1684,7 +1684,7 @@
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Android TV құрылғыңыздың құлпын <xliff:g id="NUMBER">%d</xliff:g> рет дұрыс ашпадыңыз. Енді Android TV құрылғыңыздың зауыттық әдепкі параметрлері қайтарылады."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Телефонды ашуға <xliff:g id="NUMBER">%d</xliff:g> рет қате әрекеттендіңіз. Телефон бастапқы зауыттық параметрлеріне қайта реттеледі."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате сыздыңыз. After <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін планшетіңізді аккаунт арқылы ашу өтінішін аласыз.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс сызбадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың құлпын ашу үшін есептік жазбаңызға кіру керек болады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталап көріңіз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс сызбадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың құлпын ашу үшін аккаунтыңызға кіру керек болады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталап көріңіз."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате сыздыңыз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін телефоныңызды аккаунт арқылы ашу өтінішін аласыз. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Жою"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index bff48b9..a2aa0ce 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -321,7 +321,7 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"piekļūt jūsu fiziskajām aktivitātēm"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"uzņemt attēlus un ierakstīt videoklipus"</string>
- <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Tuvējās ierīces"</string>
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Tuvumā esošas ierīces"</string>
<string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"tuvumā esošu ierīču meklēšana un savienojuma izveide ar tām"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Zvanu žurnāli"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"lasīt un rakstīt tālruņa zvanu žurnālu"</string>
@@ -542,7 +542,7 @@
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Ļauj lietotnei piekļūt pārī savienotām Bluetooth ierīcēm."</string>
<string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"reklamēšana tuvumā esošās Bluetooth ierīcēs"</string>
<string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Atļauj lietotnei veikt reklamēšanu tuvumā esošās Bluetooth ierīcēs"</string>
- <string name="permlab_uwb_ranging" msgid="8141915781475770665">"novietojuma noteikšana starp tuvu esošām ultraplatjoslas ierīcēm"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"novietojums starp tuvējām ultraplatjoslas ierīcēm"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Atļaut lietotnei noteikt relatīvo atrašanās vietu starp tuvumā esošām ultraplatjoslas ierīcēm"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informācija par vēlamo NFC maksājumu pakalpojumu"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ļauj lietotnei iegūt informāciju par vēlamo NFC maksājumu pakalpojumu, piemēram, par reģistrētajiem lietojumprogrammu ID un maršruta galamērķi."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 2acc13d..a5e2cbc 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -850,7 +850,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"മറ്റുള്ളവ"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"ഇഷ്ടാനുസൃതം"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"ഇഷ്ടാനുസൃതം"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"അസിസ്റ്റന്റ്"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"സഹോദരന്"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"കുട്ടി"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"ഗാര്ഹിക പങ്കാളി"</string>
@@ -2010,7 +2010,7 @@
<string name="app_category_image" msgid="7307840291864213007">"ഫോട്ടോകളും ചിത്രങ്ങളും"</string>
<string name="app_category_social" msgid="2278269325488344054">"സാമൂഹിക ആപ്സുകളും ആശയവിനിമയവും"</string>
<string name="app_category_news" msgid="1172762719574964544">"വാർത്തകളും മാസികകളും"</string>
- <string name="app_category_maps" msgid="6395725487922533156">"Maps & Navigation"</string>
+ <string name="app_category_maps" msgid="6395725487922533156">"മാപ്പുകളും നാവിഗേഷനും"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ഉല്പ്പാദനക്ഷമത"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"ഉപയോഗസഹായി"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ഉപകരണ സ്റ്റോറേജ്"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 23c37b7..eeb2b57 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -408,7 +408,7 @@
<string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः सुरू करण्यास अनुमती देते. यामुळे फोन सुरू करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर फोनला धीमे करण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_broadcastSticky" msgid="4552241916400572230">"रोचक प्रसारण पाठवा"</string>
<string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
- <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"चिकट प्रसारणे पाठविण्यासाठी ॲपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो तुमच्या Android TV डिव्हाइसला धीमा किंवा अस्थिर करू शकतो."</string>
+ <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"रोचक प्रसारणे पाठविण्यासाठी अॅपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो तुमच्या Android TV डिव्हाइसला धिमा किंवा अस्थिर करू शकतो."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permlab_readContacts" msgid="8776395111787429099">"तुमचे संपर्क वाचा"</string>
<string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"तुमच्या टॅबलेटवर स्टोअर केलेल्या तुमच्या संपर्कांविषयीचा डेटा वाचण्याची ॲपला अनुमती देते. अॅप्सना संपर्क तयार केलेल्या तुमच्या टॅबलेटवरील खात्याचा अॅक्सेसदेखील असेल. यामध्ये तुम्ही इंस्टॉल केलेल्या ॲप्सने तयार केलेल्या खात्यांचा समावेश असू शकतात. ही परवानगी ॲप्सना तुमचा संपर्क डेटा सेव्ह करण्याची अनुमती देते आणि दुर्भावनापूर्ण ॲप्स तुम्हाला न कळवता संपर्क डेटा शेअर करू शकतात."</string>
@@ -985,7 +985,7 @@
<string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"या पेजवर रहा"</string>
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nआपल्याला खात्री आहे की तुम्ही या पृष्ठावरून नेव्हिगेट करू इच्छिता?"</string>
<string name="save_password_label" msgid="9161712335355510035">"पुष्टी करा"</string>
- <string name="double_tap_toast" msgid="7065519579174882778">"टीप: झूम कमी करण्यासाठी आणि वाढवण्यासाठी दोनदा-टॅप करा."</string>
+ <string name="double_tap_toast" msgid="7065519579174882778">"टीप: झूम कमी करण्यासाठी आणि वाढवण्यासाठी दोनदा टॅप करा."</string>
<string name="autofill_this_form" msgid="3187132440451621492">"स्वयं-भरण"</string>
<string name="setup_autofill" msgid="5431369130866618567">"स्वयं-भरण सेट करा"</string>
<string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> सह ऑटोफील करा"</string>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 2e4578c..783fabe 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -33,4 +33,6 @@
<color name="call_notification_answer_color">#5DBA80</color>
<color name="personal_apps_suspension_notification_color">#8AB4F8</color>
+
+ <color name="overview_background">@color/overview_background_dark</color>
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 53eb2b7..7b80f60 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -2010,7 +2010,7 @@
<string name="app_category_image" msgid="7307840291864213007">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਚਿੱਤਰ"</string>
<string name="app_category_social" msgid="2278269325488344054">"ਸਮਾਜਕ ਅਤੇ ਸੰਚਾਰ"</string>
<string name="app_category_news" msgid="1172762719574964544">"ਖਬਰਾਂ ਅਤੇ ਰਸਾਲੇ"</string>
- <string name="app_category_maps" msgid="6395725487922533156">"ਨਕਸ਼ੇ ਅਤੇ ਆਵਾਗੌਣ"</string>
+ <string name="app_category_maps" msgid="6395725487922533156">"Maps ਅਤੇ ਨੈਵੀਗੇਸ਼ਨ"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ਉਤਪਾਦਕਤਾ"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"ਪਹੁੰਚਯੋਗਤਾ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ਡੀਵਾਈਸ ਸਟੋਰੇਜ"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index cdaf254..da3164a 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -2039,7 +2039,7 @@
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
<string name="autofill_save_yes" msgid="8035743017382012850">"Salvar"</string>
- <string name="autofill_save_no" msgid="9212826374207023544">"Não, obrigado"</string>
+ <string name="autofill_save_no" msgid="9212826374207023544">"Agora não"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Agora não"</string>
<string name="autofill_save_never" msgid="6821841919831402526">"Nunca"</string>
<string name="autofill_update_yes" msgid="4608662968996874445">"Atualizar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index cdaf254..da3164a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -2039,7 +2039,7 @@
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
<string name="autofill_save_yes" msgid="8035743017382012850">"Salvar"</string>
- <string name="autofill_save_no" msgid="9212826374207023544">"Não, obrigado"</string>
+ <string name="autofill_save_no" msgid="9212826374207023544">"Agora não"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Agora não"</string>
<string name="autofill_save_never" msgid="6821841919831402526">"Nunca"</string>
<string name="autofill_update_yes" msgid="4608662968996874445">"Atualizar"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 13de370..1baaa1f 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2110,7 +2110,7 @@
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"மொபைலில் போதுமான சார்ஜ் உள்ளது. அம்சங்கள் இனி தடையின்றி இயங்கும்."</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"டேப்லெட்டில் போதுமான சார்ஜ் உள்ளது. அம்சங்கள் இனி தடையின்றி இயங்கும்."</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"சாதனத்தில் போதுமான சார்ஜ் உள்ளது. அம்சங்கள் இனி தடையின்றி இயங்கும்."</string>
- <string name="mime_type_folder" msgid="2203536499348787650">"கோப்புறை"</string>
+ <string name="mime_type_folder" msgid="2203536499348787650">"ஃபோல்டர்"</string>
<string name="mime_type_apk" msgid="3168784749499623902">"Android ஆப்ஸ்"</string>
<string name="mime_type_generic" msgid="4606589110116560228">"ஃபைல்"</string>
<string name="mime_type_generic_ext" msgid="9220220924380909486">"<xliff:g id="EXTENSION">%1$s</xliff:g> ஃபைல்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index b1af678c..a75ef3d 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -79,8 +79,8 @@
<string name="CLIRPermanent" msgid="166443681876381118">"మీరు కాలర్ ID సెట్టింగ్ను మార్చలేరు."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"మొబైల్ డేటా సేవ లేదు"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"అత్యవసర కాలింగ్ అందుబాటులో లేదు"</string>
- <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"వాయిస్ సేవ లేదు"</string>
- <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"వాయిస్ సేవ లేదా అత్యవసర కాలింగ్ లేదు"</string>
+ <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"వాయిస్ సర్వీస్ లేదు"</string>
+ <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"వాయిస్ సర్వీస్ లేదా ఎమర్జెన్సీ కాలింగ్ లేదు"</string>
<string name="RestrictedStateContent" msgid="7693575344608618926">"మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేయబడింది"</string>
<string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> కోసం మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేశారు"</string>
<string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"మొబైల్ నెట్వర్క్ అందుబాటులో లేదు"</string>
@@ -306,7 +306,7 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"మీ కాంటాక్ట్లను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"లొకేషన్"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"ఈ పరికర లొకేషన్ను యాక్సెస్ చేయడానికి"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"క్యాలెండర్"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"మీ క్యాలెండర్ను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్లను పంపడం మరియు వీక్షించడం"</string>
@@ -453,9 +453,9 @@
<string name="permlab_activityRecognition" msgid="1782303296053990884">"భౌతిక కార్యాకలాపాన్ని గుర్తించండి"</string>
<string name="permdesc_activityRecognition" msgid="8667484762991357519">"ఈ యాప్ మీ భౌతిక కార్యాకలాపాన్ని గుర్తించగలదు."</string>
<string name="permlab_camera" msgid="6320282492904119413">"చిత్రాలు మరియు వీడియోలు తీయడం"</string>
- <string name="permdesc_camera" msgid="5240801376168647151">"యాప్ ఉపయోగంలో ఉన్నపుడు కెమెరాను ఉపయోగించి ఈ యాప్ ఎప్పుడైనా ఫోటోలను తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు."</string>
+ <string name="permdesc_camera" msgid="5240801376168647151">"యాప్ ఉపయోగంలో ఉన్నపుడు కెమెరాను ఉపయోగించి ఎప్పుడు కావాలంటే అప్పుడు ఈ యాప్ ఫోటోలు తీయగలదు, వీడియోలు రికార్డ్ చేయగలదు."</string>
<string name="permlab_backgroundCamera" msgid="7549917926079731681">"బ్యాక్గ్రౌండ్లో ఫోటోలు, వీడియోలను తీయగలదు"</string>
- <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"కెమెరాను ఉపయోగించి ఈ యాప్ ఎప్పుడైనా ఫోటోలను తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు."</string>
+ <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ఈ యాప్, కెమెరాను ఉపయోగించి ఎప్పుడు కావాలంటే అప్పుడు ఫోటోలు తీయగలదు, వీడియోలు రికార్డ్ చేయగలదు."</string>
<string name="permlab_systemCamera" msgid="3642917457796210580">"ఫోటోలు, వీడియోలు తీయడానికి సిస్టమ్ కెమెరాలకు యాప్, లేదా సేవా యాక్సెస్ను అనుమతించండి"</string>
<string name="permdesc_systemCamera" msgid="5938360914419175986">"ఈ విశేష లేదా సిస్టమ్ యాప్ ఎప్పుడైనా సిస్టమ్ కెమెరాను ఉపయోగించి ఫోటోలు తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు. యాప్కు android.permission.CAMERA అనుమతి ఇవ్వడం కూడా అవసరం"</string>
<string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"కెమెరా పరికరాలు తెరుచుకుంటున్నప్పుడు లేదా మూసుకుంటున్నప్పుడు కాల్బ్యాక్లను స్వీకరించడానికి యాప్ను లేదా సర్వీస్ను అనుమతించండి."</string>
@@ -817,7 +817,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"కార్యాలయ మొబైల్"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"కార్యాలయ పేజర్"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"అసిస్టెంట్"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"అనుకూలం"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"పుట్టినరోజు"</string>
@@ -850,7 +850,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"ఇతరం"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"అనుకూలం"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"అనుకూలం"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"అసిస్టెంట్"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"సోదరుడు"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"బిడ్డ"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"జీవిత భాగస్వామి"</string>
@@ -1043,10 +1043,10 @@
<string name="search_go" msgid="2141477624421347086">"సెర్చ్"</string>
<string name="search_hint" msgid="455364685740251925">"సెర్చ్ చేయండి..."</string>
<string name="searchview_description_search" msgid="1045552007537359343">"సెర్చ్"</string>
- <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను వెతకండి"</string>
+ <string name="searchview_description_query" msgid="7430242366971716338">"సెర్చ్ క్వెరీ"</string>
<string name="searchview_description_clear" msgid="1989371719192982900">"ప్రశ్నను క్లియర్ చేయి"</string>
<string name="searchview_description_submit" msgid="6771060386117334686">"ప్రశ్నని సమర్పించండి"</string>
- <string name="searchview_description_voice" msgid="42360159504884679">"వాయిస్ శోధన"</string>
+ <string name="searchview_description_voice" msgid="42360159504884679">"వాయిస్ సెర్చ్"</string>
<string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలా?"</string>
<string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణను ఆన్ చేసినప్పుడు, మీరు మీ వేలి కింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా టాబ్లెట్తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string>
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణ ఆన్ చేయబడినప్పుడు, మీరు మీ వేలి కింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా ఫోన్తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string>
@@ -1412,7 +1412,7 @@
<string name="ext_media_new_notification_message" msgid="6095403121990786986">"సెటప్ చేయడానికి నొక్కండి"</string>
<string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"సెటప్ చేయడానికి ఎంచుకోండి"</string>
<string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"మీరు పరికరాన్ని తిరిగి ఫార్మాట్ చేయాల్సి ఉంటుంది. తొలగించడానికి ట్యాప్ చేయండి"</string>
- <string name="ext_media_ready_notification_message" msgid="777258143284919261">"ఫోటోలు మరియు మీడియాను బదిలీ చేయడానికి"</string>
+ <string name="ext_media_ready_notification_message" msgid="777258143284919261">"ఫోటోలు, మీడియాను బదిలీ చేయడానికి"</string>
<string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"మీడియా ఫైల్స్ను బ్రౌజ్ చేయండి"</string>
<string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"<xliff:g id="NAME">%s</xliff:g>తో సమస్య ఉంది"</string>
<string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> పని చేయటం లేదు"</string>
@@ -1524,7 +1524,7 @@
<string name="progress_erasing" msgid="6891435992721028004">"షేర్ చేసిన నిల్వను తొలగిస్తోంది…"</string>
<string name="share" msgid="4157615043345227321">"షేర్"</string>
<string name="find" msgid="5015737188624767706">"కనుగొనండి"</string>
- <string name="websearch" msgid="5624340204512793290">"వెబ్ శోధన"</string>
+ <string name="websearch" msgid="5624340204512793290">"వెబ్ సెర్చ్"</string>
<string name="find_next" msgid="5341217051549648153">"తదుపరిదాన్ని కనుగొను"</string>
<string name="find_previous" msgid="4405898398141275532">"మునుపటిదాన్ని కనుగొను"</string>
<string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి లొకేషన్ రిక్వెస్ట్"</string>
@@ -1877,7 +1877,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్లు, నిర్దిష్ట ఫీచర్లు, ఇంకా కొన్ని నెట్వర్క్ కనెక్షన్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్లు, నిర్దిష్ట ఫీచర్లు, ఇంకా కొన్ని నెట్వర్క్ కనెక్షన్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్గ్రౌండ్లో కొన్ని యాప్లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్, డేటాను యాక్సెస్ చేయగలదు. కానీ తక్కువ సార్లు మాత్రమే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఫోటోలు ప్రదర్శించబడవు."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్గ్రౌండ్లో కొన్ని యాప్లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్, డేటాను యాక్సెస్ చేయగలదు. కానీ తక్కువ సార్లు మాత్రమే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఇమేజ్లు ప్రదర్శించబడవు."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"డేటా సేవర్ను ఆన్ చేయాలా?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ఆన్ చేయి"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273">
@@ -2007,7 +2007,7 @@
<string name="app_category_game" msgid="4534216074910244790">"గేమ్లు"</string>
<string name="app_category_audio" msgid="8296029904794676222">"సంగీతం & ఆడియో"</string>
<string name="app_category_video" msgid="2590183854839565814">"చలనచిత్రాలు & వీడియో"</string>
- <string name="app_category_image" msgid="7307840291864213007">"ఫోటోలు & చిత్రాలు"</string>
+ <string name="app_category_image" msgid="7307840291864213007">"ఫోటోలు, ఇమేజ్లు"</string>
<string name="app_category_social" msgid="2278269325488344054">"సామాజికం & కమ్యూనికేషన్"</string>
<string name="app_category_news" msgid="1172762719574964544">"వార్తలు & వార్తాపత్రికలు"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps & నావిగేషన్"</string>
@@ -2290,7 +2290,7 @@
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"సెట్టింగ్లలో ఆన్ చేయండి"</string>
<string name="dismiss_action" msgid="1728820550388704784">"విస్మరించు"</string>
<string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"పరికరం మైక్రోఫోన్ను అన్బ్లాక్ చేయండి"</string>
- <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"పరికరం కెమెరాను అన్బ్లాక్ చేయండి"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"పరికరంలోని కెమెరాను అన్బ్లాక్ చేయండి"</string>
<string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> యాప్, ఇతర యాప్లు, సర్వీస్ల కోసం"</string>
<string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"అన్బ్లాక్ చేయండి"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"సెన్సార్ గోప్యత"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index b70cee0..85e3110 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1757,7 +1757,7 @@
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Виберіть функції для кнопки спеціальних можливостей"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Виберіть функції для комбінації з клавішами гучності"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сервіс <xliff:g id="SERVICE_NAME">%s</xliff:g> вимкнено"</string>
- <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Редагувати засоби"</string>
+ <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Змінити"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Вимкнути ярлик"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 50a322f..15ec82c 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1073,7 +1073,7 @@
<string name="weeks" msgid="3516247214269821391">"tuần"</string>
<string name="year" msgid="5182610307741238982">"năm"</string>
<string name="years" msgid="5797714729103773425">"năm"</string>
- <string name="now_string_shortest" msgid="3684914126941650330">"ngay lúc này"</string>
+ <string name="now_string_shortest" msgid="3684914126941650330">"vừa xong"</string>
<plurals name="duration_minutes_shortest" formatted="false" msgid="7519574894537185135">
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>ph</item>
<item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>ph</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0a6b02d..5339640 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8371,16 +8371,18 @@
@hide @SystemApi -->
<attr name="supportsAmbientMode" format="boolean" />
- <!-- Indicates that this wallpaper service should receive zoom updates when unfolding.
+ <!-- Indicates that this wallpaper service should receive zoom transition updates when
+ changing the device state (e.g. when folding or unfolding a foldable device).
When this value is set to true
{@link android.service.wallpaper.WallpaperService.Engine} could receive zoom updates
- when folding or unfolding a foldable device. Wallpapers receive zoom updates using
+ before or after changing the device state. Wallpapers receive zoom updates using
{@link android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)} and
- zoom rendering should be handled manually. Default value is true.
- When set to false wallpapers can implement custom folding/unfolding behavior
- by listening to {@link android.hardware.Sensor#TYPE_HINGE_ANGLE}.
- Corresponds to {@link android.app.WallpaperInfo#shouldUseDefaultUnfoldTransition()} -->
- <attr name="shouldUseDefaultUnfoldTransition" format="boolean" />
+ zoom rendering should be handled manually. Zoom updates are delivered only when
+ {@link android.service.wallpaper.WallpaperService.Engine} is created and not destroyed.
+ Default value is true.
+ Corresponds to
+ {@link android.app.WallpaperInfo#shouldUseDefaultDeviceStateChangeTransition()} -->
+ <attr name="shouldUseDefaultDeviceStateChangeTransition" format="boolean" />
<!-- Uri that specifies a settings Slice for this wallpaper. -->
<attr name="settingsSliceUri" format="string"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e3e5f84..689d75b9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -654,6 +654,11 @@
-->
</integer-array>
+ <!-- When entering this device state (defined in device_state_configuration.xml),
+ we should wake the device. -1 to disable the feature (do not wake on any device-state
+ transition). -->
+ <integer name="config_deviceStateOnWhichToWakeUp">-1</integer>
+
<!-- Indicate the display area rect for foldable devices in folded state. -->
<string name="config_foldedArea"></string>
@@ -4528,6 +4533,13 @@
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
<integer name="config_fixedRefreshRateInHighZone">0</integer>
+ <!-- Default refresh rate while the device has high brightness mode enabled for Sunlight.
+ This value overrides values from DisplayDeviceConfig -->
+ <integer name="config_defaultRefreshRateInHbmSunlight">0</integer>
+
+ <!-- Default refresh rate while the device has high brightness mode enabled for HDR. -->
+ <integer name="config_defaultRefreshRateInHbmHdr">0</integer>
+
<!-- The type of the light sensor to be used by the display framework for things like
auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
<string name="config_displayLightSensorType" translatable="false" />
@@ -4856,8 +4868,8 @@
<item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item>
<!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
- corners of the activity won't be rounded. -->
- <integer name="config_letterboxActivityCornersRadius">0</integer>
+ min between device bottom corner radii will be used instead. -->
+ <integer name="config_letterboxActivityCornersRadius">-1</integer>
<!-- Blur radius for the Option 3 in R.integer.config_letterboxBackgroundType. Values < 0 are
ignored and 0 is used. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7d48904..e17daf0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3221,7 +3221,7 @@
<eat-comment />
<staging-public-group type="attr" first-id="0x01ff0000">
- <public name="shouldUseDefaultUnfoldTransition" />
+ <public name="shouldUseDefaultDeviceStateChangeTransition" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01fe0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b6c7659..4e8d915 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3846,6 +3846,7 @@
<!-- For Foldables -->
<java-symbol type="array" name="config_foldedDeviceStates" />
+ <java-symbol type="integer" name="config_deviceStateOnWhichToWakeUp" />
<java-symbol type="string" name="config_foldedArea" />
<java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
<java-symbol type="bool" name="config_unfoldTransitionEnabled" />
@@ -3963,6 +3964,8 @@
<java-symbol type="integer" name="config_defaultRefreshRateInZone" />
<java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
<java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
+ <java-symbol type="integer" name="config_defaultRefreshRateInHbmSunlight" />
+ <java-symbol type="integer" name="config_defaultRefreshRateInHbmHdr" />
<!-- For fixed refresh rate displays in high brightness-->
<java-symbol type="integer" name="config_fixedRefreshRateInHighZone" />
@@ -4443,6 +4446,8 @@
<java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
+ <java-symbol type="color" name="overview_background"/>
+
<java-symbol type="string" name="config_secondaryBuiltInDisplayCutout" />
<java-symbol type="string" name="config_secondaryBuiltInDisplayCutoutRectApproximation" />
<java-symbol type="bool" name="config_fillSecondaryBuiltInDisplayCutout" />
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index b7dc1c5..d4799a8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -592,7 +592,7 @@
public void testUpdateDisplayMeasuredEnergyStatsLocked() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
- bi.initMeasuredEnergyStats();
+ bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
clocks.realtime = 0;
int screen = Display.STATE_OFF;
@@ -677,7 +677,7 @@
public void testUpdateCustomMeasuredEnergyStatsLocked_neverCalled() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
- bi.initMeasuredEnergyStats();
+ bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
bi.setOnBatteryInternal(true);
final int uid1 = 11500;
@@ -691,7 +691,7 @@
public void testUpdateCustomMeasuredEnergyStatsLocked() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
- bi.initMeasuredEnergyStats();
+ bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
final int bucketA = 0; // Custom bucket 0
final int bucketB = 1; // Custom bucket 1
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index b851f0a..0135fe8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -403,7 +403,7 @@
assertNotNull(sensor.getSensorBackgroundTime());
// Reset the stats. Since the sensor is still running, we should still see the timer
- bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+ bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
sensor = uid.getSensorStats().get(SENSOR_ID);
assertNotNull(sensor);
@@ -413,7 +413,7 @@
bi.noteStopSensorLocked(UID, SENSOR_ID);
// Now the sensor timer has stopped so this reset should also take out the sensor.
- bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+ bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
sensor = uid.getSensorStats().get(SENSOR_ID);
assertNull(sensor);
@@ -465,7 +465,7 @@
// Reset the stats. Since the sensor is still running, we should still see the timer
// but still with 0 times.
- bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+ bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
assertEquals(0, timer.getTotalTimeLocked(1000*clocks.realtime, which));
assertEquals(0, timer.getTotalDurationMsLocked(clocks.realtime));
assertEquals(0, bgTimer.getTotalTimeLocked(1000*clocks.realtime, which));
@@ -504,7 +504,7 @@
// Reset the stats. Since the sensor is still running, we should still see the timer
// but with 0 times.
- bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+ bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
assertEquals(0, timer.getTotalTimeLocked(1000*clocks.realtime, which));
assertEquals(0, timer.getTotalDurationMsLocked(clocks.realtime));
assertEquals(0, bgTimer.getTotalTimeLocked(1000*clocks.realtime, which));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index 0147cdb..74b6dbe 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -18,6 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.app.ActivityManager;
import android.content.Context;
import android.os.BatteryConsumer;
@@ -263,6 +266,39 @@
.of(180.0);
}
+ @Test
+ public void testAggregateBatteryStats_incompatibleSnapshot() {
+ Context context = InstrumentationRegistry.getContext();
+ MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
+
+ BatteryUsageStatsStore batteryUsageStatsStore = mock(BatteryUsageStatsStore.class);
+
+ when(batteryUsageStatsStore.listBatteryUsageStatsTimestamps())
+ .thenReturn(new long[]{1000, 2000});
+
+ when(batteryUsageStatsStore.loadBatteryUsageStats(1000)).thenReturn(
+ new BatteryUsageStats.Builder(batteryStats.getCustomEnergyConsumerNames())
+ .setStatsDuration(1234).build());
+
+ // Add a snapshot, with a different set of custom power components. It should
+ // be skipped by the aggregation.
+ when(batteryUsageStatsStore.loadBatteryUsageStats(2000)).thenReturn(
+ new BatteryUsageStats.Builder(new String[]{"different"})
+ .setStatsDuration(4321).build());
+
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
+ batteryStats, batteryUsageStatsStore);
+
+ BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .aggregateSnapshots(0, 3000)
+ .build();
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+ assertThat(stats.getCustomPowerComponentNames())
+ .isEqualTo(batteryStats.getCustomEnergyConsumerNames());
+ assertThat(stats.getStatsDuration()).isEqualTo(1234);
+ }
+
private static class TestHandler extends Handler {
TestHandler() {
super(Looper.getMainLooper());
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 99d576d..cee1a03 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -57,11 +57,10 @@
this(new MockClocks());
}
- public void initMeasuredEnergyStats() {
+ public void initMeasuredEnergyStats(String[] customBucketNames) {
final boolean[] supportedStandardBuckets =
new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
Arrays.fill(supportedStandardBuckets, true);
- final String[] customBucketNames = {"FOO", "BAR"};
mGlobalMeasuredEnergyStats =
new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
}
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 42d80e5..8705067 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -48,6 +48,7 @@
<permission name="android.permission.SET_ACTIVITY_WATCHER"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.car.permission.CONTROL_APP_BLOCKING"/>
<!-- use for rotary fragment to enable/disable packages related to rotary -->
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6adac13..791aeb7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -553,6 +553,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-1501564055": {
+ "message": "Organized TaskFragment is not ready= %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
"-1499134947": {
"message": "Removing starting %s from %s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 884d27f..9feb619 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -827,7 +827,7 @@
case RAW_SENSOR:
return 16;
case YCBCR_P010:
- return 20;
+ return 24;
case RAW_DEPTH10:
case RAW10:
return 10;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
deleted file mode 100644
index ce4e103..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
-import androidx.window.extensions.organizer.EmbeddingExtensionImpl;
-
-/**
- * Provider class that will instantiate the library implementation. It must be included in the
- * vendor library, and the vendor implementation must match the signature of this class.
- */
-public class ExtensionProvider {
- /**
- * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
- * an OEM by overriding this method.
- */
- public static ExtensionInterface getExtensionImpl(Context context) {
- return new SampleExtensionImpl(context);
- }
-
- /** Provides a reference implementation of {@link ActivityEmbeddingComponent}. */
- public static ActivityEmbeddingComponent getActivityEmbeddingExtensionImpl(
- @NonNull Context context) {
- return new EmbeddingExtensionImpl();
- }
-
- /**
- * The support library will use this method to check API version compatibility.
- * @return API version string in MAJOR.MINOR.PATCH-description format.
- */
- public static String getApiVersion() {
- return "1.0.0-settings_sample";
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
deleted file mode 100644
index 6a53efe..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions;
-
-import android.app.Activity;
-
-import androidx.annotation.NonNull;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
- * class for their implementation.
- */
-abstract class StubExtension implements ExtensionInterface {
-
- private ExtensionCallback mExtensionCallback;
- private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
-
- StubExtension() {
- }
-
- @Override
- public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
- this.mExtensionCallback = extensionCallback;
- }
-
- @Override
- public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
- this.mWindowLayoutChangeListenerActivities.add(activity);
- this.onListenersChanged();
- }
-
- @Override
- public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
- this.mWindowLayoutChangeListenerActivities.remove(activity);
- this.onListenersChanged();
- }
-
- void updateWindowLayout(@NonNull Activity activity,
- @NonNull ExtensionWindowLayoutInfo newLayout) {
- if (this.mExtensionCallback != null) {
- mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
- }
- }
-
- @NonNull
- Set<Activity> getActivitiesListeningForLayoutChanges() {
- return mWindowLayoutChangeListenerActivities;
- }
-
- protected boolean hasListeners() {
- return !mWindowLayoutChangeListenerActivities.isEmpty();
- }
-
- protected abstract void onListenersChanged();
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
new file mode 100644
index 0000000..990d7b6
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+import android.app.ActivityThread;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.SplitController;
+import androidx.window.extensions.layout.WindowLayoutComponent;
+import androidx.window.extensions.layout.WindowLayoutComponentImpl;
+
+/**
+ * The reference implementation of {@link WindowExtensions} that implements the initial API version.
+ */
+public class WindowExtensionsImpl implements WindowExtensions {
+
+ private final Object mLock = new Object();
+ private volatile WindowLayoutComponent mWindowLayoutComponent;
+ private volatile SplitController mSplitController;
+
+ @Override
+ public int getVendorApiLevel() {
+ return 1;
+ }
+
+ @Override
+ public boolean isWindowLayoutComponentAvailable() {
+ return true;
+ }
+
+ @Override
+ public WindowLayoutComponent getWindowLayoutComponent() {
+ if (mWindowLayoutComponent == null) {
+ synchronized (mLock) {
+ if (mWindowLayoutComponent == null) {
+ Context context = ActivityThread.currentApplication();
+ mWindowLayoutComponent = new WindowLayoutComponentImpl(context);
+ }
+ }
+ }
+ return mWindowLayoutComponent;
+ }
+
+ /**
+ * Returns {@code true} if {@link ActivityEmbeddingComponent} is present on the device,
+ * {@code false} otherwise. If the component is not available the developer will receive a
+ * single callback with empty data or default values where possible.
+ */
+ @Override
+ public boolean isEmbeddingComponentAvailable() {
+ return true;
+ }
+
+ /**
+ * Returns the OEM implementation of {@link ActivityEmbeddingComponent} if it is supported on
+ * the device. The implementation must match the API level reported in
+ * {@link androidx.window.extensions.WindowExtensions}. An
+ * {@link UnsupportedOperationException} will be thrown if the device does not support
+ * Activity Embedding. Use
+ * {@link WindowExtensions#isEmbeddingComponentAvailable()} to determine if
+ * {@link ActivityEmbeddingComponent} is present.
+ * @return the OEM implementation of {@link ActivityEmbeddingComponent}
+ */
+ @NonNull
+ public ActivityEmbeddingComponent getActivityEmbeddingComponent() {
+ if (mSplitController == null) {
+ synchronized (mLock) {
+ if (mSplitController == null) {
+ mSplitController = new SplitController();
+ }
+ }
+ }
+ return mSplitController;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java
new file mode 100644
index 0000000..f9e1f07
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+
+/**
+ * Provides the OEM implementation of {@link WindowExtensions}.
+ */
+public class WindowExtensionsProvider {
+
+ private static final WindowExtensions sWindowExtensions = new WindowExtensionsImpl();
+
+ /**
+ * Returns the OEM implementation of {@link WindowExtensions}. This method is implemented in
+ * the library provided on the device and overwrites one in the Jetpack library included in
+ * apps.
+ * @return the OEM implementation of {@link WindowExtensions}
+ */
+ @NonNull
+ public static WindowExtensions getWindowExtensions() {
+ return sWindowExtensions;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
similarity index 98%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 46c8ffe..85ef270 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -36,7 +36,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.window.extensions.embedding.SplitRule;
import java.util.Map;
import java.util.concurrent.Executor;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
similarity index 93%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index a41557d..06e7d14 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import android.annotation.NonNull;
import android.app.Activity;
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitPlaceholderRule;
-import androidx.window.extensions.embedding.SplitRule;
-
/**
* Client-side descriptor of a split that holds two containers.
*/
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
similarity index 88%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index a783fcd..e1c8b11 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,19 +31,10 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.util.Pair;
import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
-import androidx.window.extensions.embedding.ActivityRule;
-import androidx.window.extensions.embedding.EmbeddingRule;
-import androidx.window.extensions.embedding.SplitInfo;
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitPlaceholderRule;
-import androidx.window.extensions.embedding.SplitRule;
-import androidx.window.extensions.embedding.TaskFragment;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -53,7 +44,8 @@
/**
* Main controller class that manages split states and presentation.
*/
-public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback {
+public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
+ ActivityEmbeddingComponent {
private final SplitPresenter mPresenter;
@@ -64,6 +56,7 @@
// Callback to Jetpack to notify about changes to split states.
private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
+ private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
public SplitController() {
mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
@@ -77,6 +70,7 @@
}
/** Updates the embedding rules applied to future activity launches. */
+ @Override
public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
mSplitRules.clear();
mSplitRules.addAll(rules);
@@ -103,7 +97,8 @@
/**
* Registers the split organizer callback to notify about changes to active splits.
*/
- public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> callback) {
+ @Override
+ public void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> callback) {
mEmbeddingCallback = callback;
updateCallbackIfNecessary();
}
@@ -119,8 +114,8 @@
container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
- updateCallbackIfNecessary();
}
+ updateCallbackIfNecessary();
}
@Override
@@ -139,8 +134,8 @@
final boolean shouldFinishDependent =
!taskFragmentInfo.isTaskClearedForReuse();
mPresenter.cleanupContainer(container, shouldFinishDependent);
- updateCallbackIfNecessary();
}
+ updateCallbackIfNecessary();
}
@Override
@@ -164,18 +159,23 @@
}
}
+ void onActivityCreated(@NonNull Activity launchedActivity) {
+ handleActivityCreated(launchedActivity);
+ updateCallbackIfNecessary();
+ }
+
/**
* Checks if the activity start should be routed to a particular container. It can create a new
* container for the activity and a new split container if necessary.
*/
// TODO(b/190433398): Break down into smaller functions.
- void onActivityCreated(@NonNull Activity launchedActivity) {
+ void handleActivityCreated(@NonNull Activity launchedActivity) {
final List<EmbeddingRule> splitRules = getSplitRules();
final TaskFragmentContainer currentContainer = getContainerWithActivity(
launchedActivity.getActivityToken(), launchedActivity);
// Check if the activity is configured to always be expanded.
- if (shouldExpand(launchedActivity, splitRules)) {
+ if (shouldExpand(launchedActivity, null, splitRules)) {
if (shouldContainerBeExpanded(currentContainer)) {
// Make sure that the existing container is expanded
mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
@@ -240,8 +240,6 @@
mPresenter.createNewSplitContainer(activityBelow, launchedActivity,
splitPairRule);
-
- updateCallbackIfNecessary();
}
private void onActivityConfigurationChanged(@NonNull Activity activity) {
@@ -501,7 +499,7 @@
continue;
}
SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule;
- if (placeholderRule.getActivityPredicate().test(activity)) {
+ if (placeholderRule.matchesActivity(activity)) {
return placeholderRule;
}
}
@@ -515,8 +513,16 @@
if (mEmbeddingCallback == null) {
return;
}
- // TODO(b/190433398): Check if something actually changed
- mEmbeddingCallback.accept(getActiveSplitStates());
+ if (!allActivitiesCreated()) {
+ return;
+ }
+ List<SplitInfo> currentSplitStates = getActiveSplitStates();
+ if (mLastReportedSplitStates.equals(currentSplitStates)) {
+ return;
+ }
+ mLastReportedSplitStates.clear();
+ mLastReportedSplitStates.addAll(currentSplitStates);
+ mEmbeddingCallback.accept(currentSplitStates);
}
/**
@@ -525,20 +531,46 @@
private List<SplitInfo> getActiveSplitStates() {
List<SplitInfo> splitStates = new ArrayList<>();
for (SplitContainer container : mSplitContainers) {
- TaskFragment primaryContainer =
- new TaskFragment(
+ if (container.getPrimaryContainer().isEmpty()
+ || container.getSecondaryContainer().isEmpty()) {
+ // Skipping containers that do not have any activities to report.
+ continue;
+ }
+ ActivityStack primaryContainer =
+ new ActivityStack(
container.getPrimaryContainer().collectActivities());
- TaskFragment secondaryContainer =
- new TaskFragment(
+ ActivityStack secondaryContainer =
+ new ActivityStack(
container.getSecondaryContainer().collectActivities());
SplitInfo splitState = new SplitInfo(primaryContainer,
- secondaryContainer, container.getSplitRule().getSplitRatio());
+ secondaryContainer,
+ // Splits that are not showing side-by-side are reported as having 0 split
+ // ratio, since by definition in the API the primary container occupies no
+ // width of the split when covered by the secondary.
+ mPresenter.shouldShowSideBySide(container)
+ ? container.getSplitRule().getSplitRatio()
+ : 0.0f);
splitStates.add(splitState);
}
return splitStates;
}
/**
+ * Checks if all activities that are registered with the containers have already appeared in
+ * the client.
+ */
+ private boolean allActivitiesCreated() {
+ for (TaskFragmentContainer container : mContainers) {
+ if (container.getInfo() == null
+ || container.getInfo().getActivities().size()
+ != container.collectActivities().size()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Returns {@code true} if the container is expanded to occupy full task size.
* Returns {@code false} if the container is included in an active split.
*/
@@ -567,8 +599,7 @@
continue;
}
SplitPairRule pairRule = (SplitPairRule) rule;
- if (pairRule.getActivityIntentPredicate().test(
- new Pair(primaryActivity, secondaryActivityIntent))) {
+ if (pairRule.matchesActivityIntentPair(primaryActivity, secondaryActivityIntent)) {
return pairRule;
}
}
@@ -587,10 +618,9 @@
}
SplitPairRule pairRule = (SplitPairRule) rule;
final Intent intent = secondaryActivity.getIntent();
- if (pairRule.getActivityPairPredicate().test(
- new Pair(primaryActivity, secondaryActivity))
- && (intent == null || pairRule.getActivityIntentPredicate().test(
- new Pair(primaryActivity, intent)))) {
+ if (pairRule.matchesActivityPair(primaryActivity, secondaryActivity)
+ && (intent == null
+ || pairRule.matchesActivityIntentPair(primaryActivity, intent))) {
return pairRule;
}
}
@@ -611,7 +641,7 @@
* Returns {@code true} if an Activity with the provided component name should always be
* expanded to occupy full task bounds. Such activity must not be put in a split.
*/
- private static boolean shouldExpand(@NonNull Activity activity,
+ private static boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent,
List<EmbeddingRule> splitRules) {
if (splitRules == null) {
return false;
@@ -624,7 +654,9 @@
if (!activityRule.shouldAlwaysExpand()) {
continue;
}
- if (activityRule.getActivityPredicate().test(activity)) {
+ if (activity != null && activityRule.matchesActivity(activity)) {
+ return true;
+ } else if (intent != null && activityRule.matchesIntent(intent)) {
return true;
}
}
@@ -678,11 +710,11 @@
/** Executor that posts on the main application thread. */
private static class MainThreadExecutor implements Executor {
- private final Handler handler = new Handler(Looper.getMainLooper());
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
- handler.post(r);
+ mHandler.post(r);
}
}
@@ -703,13 +735,25 @@
}
final Activity launchingActivity = (Activity) who;
- if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
+ if (shouldExpand(null, intent, getSplitRules())) {
+ setLaunchingInExpandedContainer(launchingActivity, options);
+ } else if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
setLaunchingInSameContainer(launchingActivity, intent, options);
}
return super.onStartActivity(who, intent, options);
}
+ private void setLaunchingInExpandedContainer(Activity launchingActivity, Bundle options) {
+ TaskFragmentContainer newContainer = mPresenter.createNewExpandedContainer(
+ launchingActivity);
+
+ // Amend the request to let the WM know that the activity should be placed in the
+ // dedicated container.
+ options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+ newContainer.getTaskFragmentToken());
+ }
+
/**
* Returns {@code true} if the activity that is going to be started via the
* {@code intent} should be paired with the {@code launchingActivity} and is set to be
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
similarity index 80%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index ac85ac8..25292b9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
+import android.util.LayoutDirection;
+import android.view.View;
import android.view.WindowInsets;
import android.view.WindowMetrics;
import android.window.TaskFragmentCreationParams;
@@ -32,8 +35,6 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitRule;
import java.util.concurrent.Executor;
@@ -42,13 +43,13 @@
* {@link SplitController}.
*/
class SplitPresenter extends JetpackTaskFragmentOrganizer {
- private static final int POSITION_LEFT = 0;
- private static final int POSITION_RIGHT = 1;
+ private static final int POSITION_START = 0;
+ private static final int POSITION_END = 1;
private static final int POSITION_FILL = 2;
@IntDef(value = {
- POSITION_LEFT,
- POSITION_RIGHT,
+ POSITION_START,
+ POSITION_END,
POSITION_FILL,
})
private @interface Position {}
@@ -96,13 +97,15 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect parentBounds = getParentContainerBounds(primaryActivity);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+ isLtr(primaryActivity, rule));
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
primaryActivity, primaryRectBounds, null);
// Create new empty task fragment
- TaskFragmentContainer secondaryContainer = mController.newContainer(null);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
+ rule, isLtr(primaryActivity, rule));
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
primaryActivity.getActivityToken(), secondaryRectBounds,
WINDOWING_MODE_MULTI_WINDOW);
@@ -135,11 +138,13 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect parentBounds = getParentContainerBounds(primaryActivity);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+ isLtr(primaryActivity, rule));
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
primaryActivity, primaryRectBounds, null);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+ isLtr(primaryActivity, rule));
final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
secondaryActivity, secondaryRectBounds, primaryContainer);
@@ -153,6 +158,20 @@
}
/**
+ * Creates a new expanded container.
+ */
+ TaskFragmentContainer createNewExpandedContainer(@NonNull Activity launchingActivity) {
+ final TaskFragmentContainer newContainer = mController.newContainer(null);
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ createTaskFragment(wct, newContainer.getTaskFragmentToken(),
+ launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_MULTI_WINDOW);
+
+ applyTransaction(wct);
+ return newContainer;
+ }
+
+ /**
* Creates a new container or resizes an existing container for activity to the provided bounds.
* @param activity The activity to be re-parented to the container if necessary.
* @param containerToAvoid Re-parent from this container if an activity is already in it.
@@ -197,8 +216,10 @@
void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
@Nullable Bundle activityOptions, @NonNull SplitRule rule) {
final Rect parentBounds = getParentContainerBounds(launchingActivity);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+ isLtr(launchingActivity, rule));
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+ isLtr(launchingActivity, rule));
TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
launchingActivity.getActivityToken());
@@ -231,8 +252,15 @@
// Getting the parent bounds using the updated container - it will have the recent value.
final Rect parentBounds = getParentContainerBounds(updatedContainer);
final SplitRule rule = splitContainer.getSplitRule();
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final Activity activity = splitContainer.getPrimaryContainer().getTopNonFinishingActivity();
+ if (activity == null) {
+ return;
+ }
+ final boolean isLtr = isLtr(activity, rule);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+ isLtr);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+ isLtr);
// If the task fragments are not registered yet, the positions will be updated after they
// are created again.
@@ -283,36 +311,64 @@
// TODO(b/190433398): Supply correct insets.
final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
new WindowInsets(new Rect()));
- return rule.getParentWindowMetricsPredicate().test(parentMetrics);
+ return rule.checkParentMetrics(parentMetrics);
}
@NonNull
private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
- @NonNull SplitRule rule) {
+ @NonNull SplitRule rule, boolean isLtr) {
if (!shouldShowSideBySide(parentBounds, rule)) {
return new Rect();
}
- float splitRatio = rule.getSplitRatio();
+ final float splitRatio = rule.getSplitRatio();
+ final float rtlSplitRatio = 1 - splitRatio;
switch (position) {
- case POSITION_LEFT:
- return new Rect(
- parentBounds.left,
- parentBounds.top,
- (int) (parentBounds.left + parentBounds.width() * splitRatio),
- parentBounds.bottom);
- case POSITION_RIGHT:
- return new Rect(
- (int) (parentBounds.left + parentBounds.width() * splitRatio),
- parentBounds.top,
- parentBounds.right,
- parentBounds.bottom);
+ case POSITION_START:
+ return isLtr ? getLeftContainerBounds(parentBounds, splitRatio)
+ : getRightContainerBounds(parentBounds, rtlSplitRatio);
+ case POSITION_END:
+ return isLtr ? getRightContainerBounds(parentBounds, splitRatio)
+ : getLeftContainerBounds(parentBounds, rtlSplitRatio);
case POSITION_FILL:
return parentBounds;
}
return parentBounds;
}
+ private Rect getLeftContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
+ return new Rect(
+ parentBounds.left,
+ parentBounds.top,
+ (int) (parentBounds.left + parentBounds.width() * splitRatio),
+ parentBounds.bottom);
+ }
+
+ private Rect getRightContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
+ return new Rect(
+ (int) (parentBounds.left + parentBounds.width() * splitRatio),
+ parentBounds.top,
+ parentBounds.right,
+ parentBounds.bottom);
+ }
+
+ /**
+ * Checks if a split with the provided rule should be displays in left-to-right layout
+ * direction, either always or with the current configuration.
+ */
+ private boolean isLtr(@NonNull Context context, @NonNull SplitRule rule) {
+ switch (rule.getLayoutDirection()) {
+ case LayoutDirection.LOCALE:
+ return context.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
+ case LayoutDirection.RTL:
+ return false;
+ case LayoutDirection.LTR:
+ default:
+ return true;
+ }
+ }
+
@NonNull
Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
final Configuration parentConfig = mFragmentParentConfigs.get(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
similarity index 88%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index 155c649..06f6228 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
+import android.graphics.Point;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.RemoteAnimationTarget;
@@ -24,6 +25,7 @@
import android.view.animation.Transformation;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
* Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
@@ -33,6 +35,7 @@
private final RemoteAnimationTarget mTarget;
private final SurfaceControl mLeash;
private final boolean mSizeChanged;
+ private final Point mPosition;
private final Transformation mTransformation = new Transformation();
private final float[] mMatrix = new float[9];
private final float[] mVecs = new float[4];
@@ -41,7 +44,7 @@
TaskFragmentAnimationAdapter(@NonNull Animation animation,
@NonNull RemoteAnimationTarget target) {
- this(animation, target, target.leash, false /* sizeChanged */);
+ this(animation, target, target.leash, false /* sizeChanged */, null /* position */);
}
/**
@@ -49,11 +52,14 @@
*/
TaskFragmentAnimationAdapter(@NonNull Animation animation,
@NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
- boolean sizeChanged) {
+ boolean sizeChanged, @Nullable Point position) {
mAnimation = animation;
mTarget = target;
mLeash = leash;
mSizeChanged = sizeChanged;
+ mPosition = position != null
+ ? position
+ : new Point(target.localBounds.left, target.localBounds.top);
}
/** Called on frame update. */
@@ -65,8 +71,7 @@
currentPlayTime = Math.min(currentPlayTime, mAnimation.getDuration());
mAnimation.getTransformation(currentPlayTime, mTransformation);
- mTransformation.getMatrix().postTranslate(
- mTarget.localBounds.left, mTarget.localBounds.top);
+ mTransformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
similarity index 97%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
index 6631243..6579766 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
similarity index 96%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index da3d116..3980d07 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
@@ -23,6 +23,7 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
+import android.graphics.Point;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -193,10 +194,12 @@
if (target.startBounds != null) {
final Animation[] animations =
mAnimationSpec.createChangeBoundsChangeAnimations(target);
+ // The snapshot surface will always be at (0, 0) of its parent.
adapters.add(new TaskFragmentAnimationAdapter(animations[0], target,
- target.startLeash, false /* sizeChanged */));
+ target.startLeash, false /* sizeChanged */, new Point(0, 0)));
+ // The end surface will have size change for scaling.
adapters.add(new TaskFragmentAnimationAdapter(animations[1], target,
- target.leash, true /* sizeChanged */));
+ target.leash, true /* sizeChanged */, null /* position */));
continue;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
similarity index 92%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationSpec.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index ddcb27d..11a79b2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
@@ -32,6 +32,7 @@
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;
@@ -46,12 +47,13 @@
private static final String TAG = "TaskFragAnimationSpec";
private static final int CHANGE_ANIMATION_DURATION = 517;
- private static final int CHANGE_ANIMATION_FADE_DURATION = 82;
- private static final int CHANGE_ANIMATION_FADE_OFFSET = 67;
+ 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) {
@@ -61,6 +63,7 @@
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();
@@ -118,9 +121,11 @@
* the second one is for the end leash.
*/
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.localBounds;
+ 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
@@ -129,14 +134,15 @@
float startScaleY = 1.f / scaleY;
// The start leash will be fade out.
- final AnimationSet startSet = new AnimationSet(true /* shareInterpolator */);
- startSet.setInterpolator(mFastOutExtraSlowInInterpolator);
+ 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(),
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
similarity index 98%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 8503b9f..54e44a7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -212,7 +212,9 @@
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
// Finish own activities
for (Activity activity : collectActivities()) {
- activity.finish();
+ if (!activity.isFinishing()) {
+ activity.finish();
+ }
}
if (!shouldFinishDependent) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
similarity index 63%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index a0d5b00..383d91d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions;
+package androidx.window.extensions.layout;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -36,19 +36,27 @@
import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
/**
- * Reference implementation of androidx.window.extensions OEM interface for use with
+ * Reference implementation of androidx.window.extensions.layout OEM interface for use with
* WindowManager Jetpack.
*
* NOTE: This version is a work in progress and under active development. It MUST NOT be used in
* production builds since the interface can still change before reaching stable version.
* Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
*/
-class SampleExtensionImpl extends StubExtension {
+public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private static final String TAG = "SampleExtension";
+ private static WindowLayoutComponent sInstance;
+
+ private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
+ new HashMap<>();
private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
private final DataProducer<Integer> mDevicePostureProducer;
@@ -56,7 +64,7 @@
private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
- SampleExtensionImpl(Context context) {
+ public WindowLayoutComponentImpl(Context context) {
mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
mDevicePostureProducer = new PriorityDataProducer<>(List.of(
mSettingsDevicePostureProducer,
@@ -73,28 +81,68 @@
mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
+ /**
+ * Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
+ * @param activity hosting a {@link android.view.Window}
+ * @param consumer interested in receiving updates to {@link WindowLayoutInfo}
+ */
+ public void addWindowLayoutInfoListener(@NonNull Activity activity,
+ @NonNull Consumer<WindowLayoutInfo> consumer) {
+ mWindowLayoutChangeListeners.put(activity, consumer);
+ updateRegistrations();
+ }
+
+ /**
+ * Removes a listener no longer interested in receiving updates.
+ * @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo}
+ */
+ public void removeWindowLayoutInfoListener(
+ @NonNull Consumer<WindowLayoutInfo> consumer) {
+ mWindowLayoutChangeListeners.values().remove(consumer);
+ updateRegistrations();
+ }
+
+ void updateWindowLayout(@NonNull Activity activity,
+ @NonNull WindowLayoutInfo newLayout) {
+ Consumer<WindowLayoutInfo> consumer = mWindowLayoutChangeListeners.get(activity);
+ if (consumer != null) {
+ consumer.accept(newLayout);
+ }
+ }
+
+ @NonNull
+ Set<Activity> getActivitiesListeningForLayoutChanges() {
+ return mWindowLayoutChangeListeners.keySet();
+ }
+
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListeners.isEmpty();
+ }
+
private int getFeatureState(DisplayFeature feature) {
Integer featureState = feature.getState();
Optional<Integer> posture = mDevicePostureProducer.getData();
- int fallbackPosture = posture.orElse(ExtensionFoldingFeature.STATE_FLAT);
+ int fallbackPosture = posture.orElse(FoldingFeature.STATE_FLAT);
return featureState == null ? fallbackPosture : featureState;
}
private void onDisplayFeaturesChanged() {
for (Activity activity : getActivitiesListeningForLayoutChanges()) {
- ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
+ WindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
updateWindowLayout(activity, newLayout);
}
}
@NonNull
- private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
- List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
- return new ExtensionWindowLayoutInfo(displayFeatures);
+ private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
+ List<androidx.window.extensions.layout.DisplayFeature> displayFeatures =
+ getDisplayFeatures(activity);
+ return new WindowLayoutInfo(displayFeatures);
}
- private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
- List<ExtensionDisplayFeature> features = new ArrayList<>();
+ private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
+ @NonNull Activity activity) {
+ List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
@@ -115,15 +163,14 @@
rotateRectToDisplayRotation(displayId, featureRect);
transformToWindowSpaceRect(activity, featureRect);
- features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+ features.add(new FoldingFeature(featureRect, baseFeature.getType(),
getFeatureState(baseFeature)));
}
}
return features;
}
- @Override
- protected void onListenersChanged() {
+ private void updateRegistrations() {
if (hasListeners()) {
mSettingsDevicePostureProducer.registerObserversIfNeeded();
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
deleted file mode 100644
index 9a8961f..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions.organizer;
-
-import androidx.annotation.NonNull;
-import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
-import androidx.window.extensions.embedding.EmbeddingRule;
-import androidx.window.extensions.embedding.SplitInfo;
-
-import java.util.List;
-import java.util.Set;
-import java.util.function.Consumer;
-
-/**
- * Reference implementation of the activity embedding interface defined in WM Jetpack.
- */
-public class EmbeddingExtensionImpl implements ActivityEmbeddingComponent {
-
- private final SplitController mSplitController;
-
- public EmbeddingExtensionImpl() {
- mSplitController = new SplitController();
- }
-
- @Override
- public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
- mSplitController.setEmbeddingRules(rules);
- }
-
- @Override
- public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> consumer) {
- mSplitController.setEmbeddingCallback(consumer);
- }
-}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 097febf..42e829e 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/color/split_divider_background.xml b/libs/WindowManager/Shell/res/color/split_divider_background.xml
index 84f4fdf..329e5b9 100644
--- a/libs/WindowManager/Shell/res/color/split_divider_background.xml
+++ b/libs/WindowManager/Shell/res/color/split_divider_background.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 1ace3cd..3258385 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -19,10 +19,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"বন্ধ কৰক"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"বিস্তাৰ কৰক"</string>
- <string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিংসমূহ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিং"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"মেনু"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> চিত্ৰৰ ভিতৰৰ চিত্ৰত আছে"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g> সুবিধাটো ব্যৱহাৰ কৰিব নোখোজে, তেন্তে ছেটিংসমূহ খুলিবলৈ টিপক আৰু তালৈ গৈ ইয়াক অফ কৰক।"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g> সুবিধাটো ব্যৱহাৰ কৰিব নোখোজে, তেন্তে ছেটিং খুলিবলৈ টিপক আৰু তালৈ গৈ ইয়াক অফ কৰক।"</string>
<string name="pip_play" msgid="3496151081459417097">"প্লে কৰক"</string>
<string name="pip_pause" msgid="690688849510295232">"পজ কৰক"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
@@ -31,25 +31,25 @@
<string name="accessibility_action_pip_stash" msgid="4060775037619702641">"লুকুৱাওক"</string>
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
- <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপ্টোৱে বিভাজিত স্ক্ৰীন সমৰ্থন নকৰে।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"গৌণ ডিছপ্লেত এপ্ লঞ্চ কৰিব নোৱাৰি।"</string>
- <string name="accessibility_divider" msgid="703810061635792791">"স্প্লিট স্ক্ৰীণৰ বিভাজক"</string>
- <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাওঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"স্প্লিট স্ক্ৰীনৰ বিভাজক"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাওঁফালৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"বাওঁফালৰ স্ক্ৰীণখন ৭০% কৰক"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"বাওঁফালৰ স্ক্ৰীণখন ৫০% কৰক"</string>
<string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"বাওঁফালৰ স্ক্ৰীণখন ৩০% কৰক"</string>
- <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
- <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ স্ক্ৰীণখন ৭০% কৰক"</string>
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ স্ক্ৰীণখন ৫০% কৰক"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"শীর্ষ স্ক্ৰীণখন ৩০% কৰক"</string>
- <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"তলৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"তলৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড ব্যৱহাৰ কৰা"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"বাহিৰ হ’বলৈ স্ক্ৰীনখনৰ একেবাৰে তলৰ পৰা ওপৰলৈ ছোৱাইপ কৰক অথবা এপ্টোৰ ওপৰত যিকোনো ঠাইত টিপক"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"এখন হাতেৰে ব্যৱহাৰ কৰা ম\'ডটো আৰম্ভ কৰক"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"এখন হাতেৰে ব্যৱহাৰ কৰা ম\'ডটোৰ পৰা বাহিৰ হওক"</string>
- <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ bubblesৰ ছেটিংসমূহ"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ bubblesৰ ছেটিং"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"অভাৰফ্ল’"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ষ্টেকত পুনৰ যোগ দিয়ক"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>ৰ পৰা <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -58,7 +58,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"বুটামটো বাওঁফালে নিয়ক"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"তলৰ সোঁফালে নিয়ক"</string>
- <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিংসমূহ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিং"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল অগ্ৰাহ্য কৰক"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"বাৰ্তালাপ বাবল নকৰিব"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
index 6c223f4..170b2db 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -20,5 +20,5 @@
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"চিত্ৰৰ ভিতৰত চিত্ৰ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
<string name="pip_close" msgid="9135220303720555525">"পিপ বন্ধ কৰক"</string>
- <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীণ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীন"</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index 0a1cd22..74672a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -28,37 +28,40 @@
import com.android.wm.shell.R
import com.android.wm.shell.animation.PhysicsAnimator
import com.android.wm.shell.common.DismissCircleView
+import android.view.WindowInsets
+import android.view.WindowManager
/*
* View that handles interactions between DismissCircleView and BubbleStackView.
*/
class DismissView(context: Context) : FrameLayout(context) {
- var circle = DismissCircleView(context).apply {
- val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
- val newParams = LayoutParams(targetSize, targetSize)
- newParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
- setLayoutParams(newParams)
- setTranslationY(
- resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height).toFloat())
- }
-
+ var circle = DismissCircleView(context)
var isShowing = false
+
private val animator = PhysicsAnimator.getInstance(circle)
private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
private val DISMISS_SCRIM_FADE_MS = 200
+ private var wm: WindowManager =
+ context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
init {
setLayoutParams(LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
Gravity.BOTTOM))
- setPadding(0, 0, 0, resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+ updatePadding()
setClipToPadding(false)
setClipChildren(false)
setVisibility(View.INVISIBLE)
setBackgroundResource(
R.drawable.floating_dismiss_gradient_transition)
- addView(circle)
+
+ val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ addView(circle, LayoutParams(targetSize, targetSize,
+ Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL))
+ // start with circle offscreen so it's animated up
+ circle.setTranslationY(resources.getDimensionPixelSize(
+ R.dimen.floating_dismiss_gradient_height).toFloat())
}
/**
@@ -91,9 +94,21 @@
}
fun updateResources() {
- val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ updatePadding()
+ layoutParams.height = resources.getDimensionPixelSize(
+ R.dimen.floating_dismiss_gradient_height)
+
+ val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
circle.layoutParams.width = targetSize
circle.layoutParams.height = targetSize
circle.requestLayout()
}
+
+ private fun updatePadding() {
+ val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
+ val navInset = insets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.navigationBars())
+ setPadding(0, 0, 0, navInset.bottom +
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+ }
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index f9ba97f..6ea806b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -25,6 +25,7 @@
import android.util.AttributeSet;
import android.util.Property;
import android.view.GestureDetector;
+import android.view.InsetsController;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.MotionEvent;
@@ -34,7 +35,6 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
@@ -51,12 +51,6 @@
public static final long TOUCH_ANIMATION_DURATION = 150;
public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
- // TODO(b/191269755): use the value defined in InsetsController.
- private static final Interpolator RESIZE_INTERPOLATOR = Interpolators.LINEAR;
-
- // TODO(b/191269755): use the value defined in InsetsController.
- private static final int ANIMATION_DURATION_RESIZE = 300;
-
/** The task bar expanded height. Used to determine whether to insets divider bounds or not. */
private float mExpandedTaskBarHeight;
@@ -142,8 +136,8 @@
if (animate) {
ObjectAnimator animator = ObjectAnimator.ofInt(this,
DIVIDER_HEIGHT_PROPERTY, mDividerBounds.height(), mTempRect.height());
- animator.setInterpolator(RESIZE_INTERPOLATOR);
- animator.setDuration(ANIMATION_DURATION_RESIZE);
+ animator.setInterpolator(InsetsController.RESIZE_INTERPOLATOR);
+ animator.setDuration(InsetsController.ANIMATION_DURATION_RESIZE);
animator.start();
} else {
DIVIDER_HEIGHT_PROPERTY.set(this, mTempRect.height());
@@ -226,16 +220,6 @@
private void setTouching() {
setSlippery(false);
mHandle.setTouching(true, true);
- if (isLandscape()) {
- mBackground.animate().scaleX(1.4f);
- } else {
- mBackground.animate().scaleY(1.4f);
- }
- mBackground.animate()
- .setInterpolator(Interpolators.TOUCH_RESPONSE)
- .setDuration(TOUCH_ANIMATION_DURATION)
- .translationZ(mTouchElevation)
- .start();
// Lift handle as well so it doesn't get behind the background, even though it doesn't
// cast shadow.
mHandle.animate()
@@ -248,13 +232,6 @@
private void releaseTouching() {
setSlippery(true);
mHandle.setTouching(false, true);
- mBackground.animate()
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
- .translationZ(0)
- .scaleX(1f)
- .scaleY(1f)
- .start();
mHandle.animate()
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 27c8d7a..1c308a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -776,26 +776,31 @@
boolean adjustSurfaceLayoutForIme(SurfaceControl.Transaction t,
SurfaceControl dividerLeash, SurfaceControl leash1, SurfaceControl leash2,
SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
- if (mYOffsetForIme == 0) return false;
+ final boolean showDim = mDimValue1 > 0.001f || mDimValue2 > 0.001f;
+ boolean adjusted = false;
+ if (mYOffsetForIme != 0) {
+ if (dividerLeash != null) {
+ mTempRect.set(mDividerBounds);
+ mTempRect.offset(0, mYOffsetForIme);
+ t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
+ }
- if (dividerLeash != null) {
- mTempRect.set(mDividerBounds);
+ mTempRect.set(mBounds1);
mTempRect.offset(0, mYOffsetForIme);
- t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
+ t.setPosition(leash1, mTempRect.left, mTempRect.top);
+
+ mTempRect.set(mBounds2);
+ mTempRect.offset(0, mYOffsetForIme);
+ t.setPosition(leash2, mTempRect.left, mTempRect.top);
+ adjusted = true;
}
- mTempRect.set(mBounds1);
- mTempRect.offset(0, mYOffsetForIme);
- t.setPosition(leash1, mTempRect.left, mTempRect.top);
-
- mTempRect.set(mBounds2);
- mTempRect.offset(0, mYOffsetForIme);
- t.setPosition(leash2, mTempRect.left, mTempRect.top);
-
- t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
- t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
-
- return true;
+ if (showDim) {
+ t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
+ t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
+ adjusted = true;
+ }
+ return adjusted;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index e511bff..3ab0624 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -43,6 +43,7 @@
import android.view.Surface;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -64,7 +65,8 @@
/**
* Manages and manipulates the one handed states, transitions, and gesture for phones.
*/
-public class OneHandedController implements RemoteCallable<OneHandedController> {
+public class OneHandedController implements RemoteCallable<OneHandedController>,
+ DisplayChangeController.OnDisplayChangingListener {
private static final String TAG = "OneHandedController";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -106,19 +108,6 @@
private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
private OneHandedUiEventLogger mOneHandedUiEventLogger;
- /**
- * Handle rotation based on OnDisplayChangingListener callback
- */
- private final DisplayChangeController.OnDisplayChangingListener mRotationController =
- (display, fromRotation, toRotation, wct) -> {
- if (!isInitialized()) {
- return;
- }
- mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct);
- mOneHandedUiEventLogger.writeEvent(
- OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT);
- };
-
private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
new DisplayController.OnDisplaysChangedListener() {
@Override
@@ -296,7 +285,7 @@
getObserver(this::onSwipeToNotificationEnabledChanged);
mShortcutEnabledObserver = getObserver(this::onShortcutEnabledChanged);
- mDisplayController.addDisplayChangingController(mRotationController);
+ mDisplayController.addDisplayChangingController(this);
setupCallback();
registerSettingObservers(mUserId);
setupTimeoutListener();
@@ -745,6 +734,27 @@
}
/**
+ * Handles rotation based on OnDisplayChangingListener callback
+ */
+ @Override
+ public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
+ WindowContainerTransaction wct) {
+ if (!isInitialized()) {
+ return;
+ }
+
+ if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver(),
+ mUserId) || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ mContext.getContentResolver(), mUserId)) {
+ return;
+ }
+
+ mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct);
+ mOneHandedUiEventLogger.writeEvent(
+ OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT);
+ }
+
+ /**
* The interface for calls from outside the Shell, within the host process.
*/
@ExternalThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index c2bbd9e..1b2f476 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.onehanded;
-import static android.os.UserHandle.myUserId;
-
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
@@ -186,20 +184,8 @@
if (mDisplayLayout.rotation() == toRotation) {
return;
}
-
- if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(context.getContentResolver(),
- myUserId())) {
- return;
- }
-
mDisplayLayout.rotateTo(context.getResources(), toRotation);
updateDisplayBounds();
-
- if (mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- context.getContentResolver(), myUserId())) {
- // If current settings is swipe notification, skip finishOffset.
- return;
- }
finishOffset(0, TRANSITION_DIRECTION_EXIT);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index ac02075..8e5c5c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -315,13 +315,13 @@
mOneHandedController = oneHandedController;
mPipTransitionController = pipTransitionController;
mTaskStackListener = taskStackListener;
- mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
- INPUT_CONSUMER_PIP, mainExecutor);
//TODO: move this to ShellInit when PipController can be injected
mMainExecutor.execute(this::init);
}
public void init() {
+ mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
+ INPUT_CONSUMER_PIP, mMainExecutor);
mPipTransitionController.registerPipTransitionCallback(this);
mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
mPipBoundsState.setDisplayId(displayId);
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 82092ac..0fbdf90 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
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -30,6 +31,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -93,6 +95,7 @@
private int mTargetSize;
private int mDismissAreaHeight;
private float mMagneticFieldRadiusPercent = 1f;
+ private WindowInsets mWindowInsets;
private SurfaceControl mTaskLeash;
private boolean mHasDismissTargetSurface;
@@ -123,6 +126,13 @@
mContext.getDrawable(R.drawable.floating_dismiss_gradient_transition));
mTargetViewContainer.setClipChildren(false);
mTargetViewContainer.addView(mTargetView);
+ mTargetViewContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> {
+ if (!windowInsets.equals(mWindowInsets)) {
+ mWindowInsets = windowInsets;
+ updateMagneticTargetSize();
+ }
+ return windowInsets;
+ });
mMagnetizedPip = mMotionHelper.getMagnetizedPip();
mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
@@ -201,10 +211,13 @@
final Resources res = mContext.getResources();
mTargetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
+ final WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+ final Insets navInset = insets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.navigationBars());
final FrameLayout.LayoutParams newParams =
new FrameLayout.LayoutParams(mTargetSize, mTargetSize);
newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- newParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
+ newParams.bottomMargin = navInset.bottom + mContext.getResources().getDimensionPixelSize(
R.dimen.floating_dismiss_bottom_margin);
mTargetView.setLayoutParams(newParams);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 3538fd2..979aa1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -314,6 +315,12 @@
}
void scheduleRemove(Runnable onRemove) {
+ // Show the latest content as soon as possible for unlocking to home.
+ if (mActivityType == ACTIVITY_TYPE_HOME) {
+ removeImmediately();
+ onRemove.run();
+ return;
+ }
if (mScheduledRunnable != null) {
mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
mScheduledRunnable = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 4ba6aca..b673d48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -141,8 +141,9 @@
static boolean isRotationSeamless(@NonNull TransitionInfo info,
DisplayController displayController) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Display is rotating, check if it should be seamless.");
+ "Display is changing, check if it should be seamless.");
boolean checkedDisplayLayout = false;
+ boolean hasTask = false;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -166,6 +167,7 @@
return false;
}
} else if (change.getTaskInfo() != null) {
+ hasTask = true;
// We only enable seamless rotation if all the visible task windows requested it.
if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -209,8 +211,12 @@
}
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Rotation IS seamless.");
- return true;
+ // ROTATION_ANIMATION_SEAMLESS can only be requested by task.
+ if (hasTask) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Rotation IS seamless.");
+ return true;
+ }
+ return false;
}
/**
@@ -280,7 +286,6 @@
final TransitionInfo.Change change = info.getChanges().get(i);
if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
- && (change.getEndRotation() != change.getStartRotation())
&& (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
boolean isSeamless = isRotationSeamless(info, mDisplayController);
final int anim = getRotationAnimation(info);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index ada2ed2..13c670a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -186,11 +186,11 @@
t.setAlpha(mBackColorSurface, 1);
t.show(mBackColorSurface);
+ t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
t.setPosition(mAnimLeash, 0, 0);
t.setAlpha(mAnimLeash, 1);
t.show(mAnimLeash);
- t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
t.setBuffer(mScreenshotLayer, buffer);
t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
t.show(mScreenshotLayer);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 911fe07..0a3a849 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -42,6 +42,7 @@
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
@@ -332,6 +333,58 @@
}
@Test
+ public void testOneHandedEnabledRotation90ShouldHandleRotate() {
+ when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(true);
+ when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
+ false);
+ final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
+ mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0,
+ Surface.ROTATION_90, handlerWCT);
+
+ verify(mMockDisplayAreaOrganizer, atLeastOnce()).onRotateDisplay(eq(mContext),
+ eq(Surface.ROTATION_90), any(WindowContainerTransaction.class));
+ }
+
+ @Test
+ public void testOneHandedDisabledRotation90ShouldNotHandleRotate() {
+ when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(false);
+ when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
+ false);
+ final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
+ mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0,
+ Surface.ROTATION_90, handlerWCT);
+
+ verify(mMockDisplayAreaOrganizer, never()).onRotateDisplay(eq(mContext),
+ eq(Surface.ROTATION_90), any(WindowContainerTransaction.class));
+ }
+
+ @Test
+ public void testSwipeToNotificationEnabledRotation90ShouldNotHandleRotate() {
+ when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(true);
+ when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
+ true);
+ final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
+ mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0,
+ Surface.ROTATION_90, handlerWCT);
+
+ verify(mMockDisplayAreaOrganizer, never()).onRotateDisplay(eq(mContext),
+ eq(Surface.ROTATION_90), any(WindowContainerTransaction.class));
+ }
+
+ @Test
+ public void testSwipeToNotificationDisabledRotation90ShouldHandleRotate() {
+ when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(true);
+ when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
+ false);
+ final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
+ mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0,
+ Surface.ROTATION_90, handlerWCT);
+
+ verify(mMockDisplayAreaOrganizer, atLeastOnce()).onRotateDisplay(eq(mContext),
+ eq(Surface.ROTATION_90), any(WindowContainerTransaction.class));
+ }
+
+ @Test
public void testStateActive_shortcutRequestActivate_skipActions() {
when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index b0f0d71..e391713 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -583,6 +583,13 @@
.setRotate(ROTATION_ANIMATION_SEAMLESS).build())
.build();
assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessButAlert, displays));
+
+ // Not seamless if there is no changed task.
+ final TransitionInfo noTask = new TransitionInfoBuilder(TRANSIT_CHANGE)
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
+ .setRotate().build())
+ .build();
+ assertFalse(DefaultTransitionHandler.isRotationSeamless(noTask, displays));
}
class TransitionInfoBuilder {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 109b535..3544987 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -50,6 +50,7 @@
bool Properties::skipEmptyFrames = true;
bool Properties::useBufferAge = true;
bool Properties::enablePartialUpdates = true;
+bool Properties::enableRenderEffectCache = false;
DebugLevel Properties::debugLevel = kDebugDisabled;
OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7df6e2c..d224a54 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -224,6 +224,7 @@
static bool skipEmptyFrames;
static bool useBufferAge;
static bool enablePartialUpdates;
+ static bool enableRenderEffectCache;
// TODO: Move somewhere else?
static constexpr float textGamma = 1.45f;
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 7556af9..48145d2 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -231,14 +231,33 @@
SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
SkPaint paint;
layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
- const auto snapshotResult = renderNode->updateSnapshotIfRequired(
- canvas->recordingContext(),
- layerProperties.getImageFilter(),
- clipBounds.roundOut()
- );
- sk_sp<SkImage> snapshotImage = snapshotResult->snapshot;
- srcBounds = snapshotResult->outSubset;
- offset = snapshotResult->outOffset;
+ sk_sp<SkImage> snapshotImage;
+ auto* imageFilter = layerProperties.getImageFilter();
+ auto recordingContext = canvas->recordingContext();
+ // On some GL vendor implementations, caching the result of
+ // getLayerSurface->makeImageSnapshot() causes a call to
+ // Fence::waitForever without a corresponding signal. This would
+ // lead to ANRs throughout the system.
+ // Instead only cache the SkImage created with the SkImageFilter
+ // for supported devices. Otherwise just create a new SkImage with
+ // the corresponding SkImageFilter each time.
+ // See b/193145089 and b/197263715
+ if (!Properties::enableRenderEffectCache) {
+ snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
+ if (imageFilter) {
+ auto subset = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
+ snapshotImage = snapshotImage->makeWithFilter(recordingContext, imageFilter,
+ subset, clipBounds.roundOut(),
+ &srcBounds, &offset);
+ }
+ } else {
+ const auto snapshotResult = renderNode->updateSnapshotIfRequired(
+ recordingContext, layerProperties.getImageFilter(), clipBounds.roundOut());
+ snapshotImage = snapshotResult->snapshot;
+ srcBounds = snapshotResult->outSubset;
+ offset = snapshotResult->outOffset;
+ }
+
const auto dstBounds = SkIRect::MakeXYWH(offset.x(),
offset.y(),
srcBounds.width(),
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index a116781..383c79b 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -146,6 +146,9 @@
LOG_ALWAYS_FATAL("Unsupported wide color space.");
}
mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension;
+
+ auto* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
+ Properties::enableRenderEffectCache = (strcmp(vendor, "Qualcomm") != 0);
}
EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index 9e2f479..c471ab1 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -24,7 +24,7 @@
<string name="label_paper_size" msgid="908654383827777759">"Размер бумаги"</string>
<string name="label_paper_size_summary" msgid="5668204981332138168">"Размер бумаги:"</string>
<string name="label_color" msgid="1108690305218188969">"Печать"</string>
- <string name="label_duplex" msgid="5370037254347072243">"Двусторонний"</string>
+ <string name="label_duplex" msgid="5370037254347072243">"С двух сторон"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
<string name="label_pages" msgid="7768589729282182230">"Страницы"</string>
<string name="destination_default_text" msgid="5422708056807065710">"Выберите принтер"</string>
diff --git a/packages/PrintSpooler/res/values-te/strings.xml b/packages/PrintSpooler/res/values-te/strings.xml
index cf0e0f6..50e6f3b 100644
--- a/packages/PrintSpooler/res/values-te/strings.xml
+++ b/packages/PrintSpooler/res/values-te/strings.xml
@@ -50,8 +50,8 @@
<string name="search" msgid="5421724265322228497">"సెర్చ్"</string>
<string name="all_printers_label" msgid="3178848870161526399">"అన్ని ప్రింటర్లు"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"సేవను జోడించు"</string>
- <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"శోధన పెట్టె చూపబడింది"</string>
- <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"శోధన పెట్టె దాచబడింది"</string>
+ <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"సెర్చ్ బాక్స్ చూపబడింది"</string>
+ <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"సెర్చ్ బాక్స్ దాచబడింది"</string>
<string name="print_add_printer" msgid="1088656468360653455">"ప్రింటర్ను జోడించు"</string>
<string name="print_select_printer" msgid="7388760939873368698">"ప్రింటర్ను ఎంచుకోండి"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"ప్రింటర్ను విస్మరించు"</string>
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 8216edf..fe7988f 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -43,6 +43,7 @@
View.OnClickListener mLearnMoreListener;
private CharSequence mContentDescription;
private CharSequence mLearnMoreContentDescription;
+ private FooterLearnMoreSpan mLearnMoreSpan;
public FooterPreference(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.footerPreferenceStyle);
@@ -68,7 +69,11 @@
if (learnMore != null && mLearnMoreListener != null) {
learnMore.setVisibility(View.VISIBLE);
SpannableString learnMoreText = new SpannableString(learnMore.getText());
- learnMoreText.setSpan(new FooterLearnMoreSpan(mLearnMoreListener), 0,
+ if (mLearnMoreSpan != null) {
+ learnMoreText.removeSpan(mLearnMoreSpan);
+ }
+ mLearnMoreSpan = new FooterLearnMoreSpan(mLearnMoreListener);
+ learnMoreText.setSpan(mLearnMoreSpan, 0,
learnMoreText.length(), 0);
learnMore.setText(learnMoreText);
if (!TextUtils.isEmpty(mLearnMoreContentDescription)) {
diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
index 54145d6..eecb4bf 100644
--- a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
@@ -34,17 +34,21 @@
android:orientation="vertical">
<ImageView
+ android:id="@+id/background_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:scaleType="centerInside"
+ android:layout_gravity="center"
+ android:adjustViewBounds="true"
android:src="@drawable/protection_background"/>
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_view"
- android:adjustViewBounds="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center" />
+ android:layout_gravity="center"
+ android:maxWidth="@dimen/settingslib_illustration_width"
+ android:maxHeight="@dimen/settingslib_illustration_height"
+ android:adjustViewBounds="true"/>
<FrameLayout
android:id="@+id/middleground_layout"
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 1f80a3e..468a976 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -17,6 +17,7 @@
package com.android.settingslib.widget;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Animatable2;
@@ -50,7 +51,9 @@
private static final String TAG = "IllustrationPreference";
private static final boolean IS_ENABLED_LOTTIE_ADAPTIVE_COLOR = false;
+ private static final int SIZE_UNSPECIFIED = -1;
+ private int mMaxHeight = SIZE_UNSPECIFIED;
private int mImageResId;
private boolean mIsAutoScale;
private Uri mImageUri;
@@ -98,6 +101,8 @@
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
+ final ImageView backgroundView =
+ (ImageView) holder.findViewById(R.id.background_view);
final FrameLayout middleGroundLayout =
(FrameLayout) holder.findViewById(R.id.middleground_layout);
final LottieAnimationView illustrationView =
@@ -115,6 +120,7 @@
illustrationFrame.setLayoutParams(lp);
handleImageWithAnimation(illustrationView);
+ handleImageFrameMaxHeight(backgroundView, illustrationView);
if (mIsAutoScale) {
illustrationView.setScaleType(mIsAutoScale
@@ -220,6 +226,19 @@
return mImageUri;
}
+ /**
+ * Sets the maximum height of the views, still use the specific one if the maximum height was
+ * larger than the specific height from XML.
+ *
+ * @param maxHeight the maximum height of the frame views in terms of pixels.
+ */
+ public void setMaxHeight(int maxHeight) {
+ if (maxHeight != mMaxHeight) {
+ mMaxHeight = maxHeight;
+ notifyChanged();
+ }
+ }
+
private void resetImageResourceCache() {
mImageDrawable = null;
mImageUri = null;
@@ -274,6 +293,23 @@
}
}
+ private void handleImageFrameMaxHeight(ImageView backgroundView, ImageView illustrationView) {
+ if (mMaxHeight == SIZE_UNSPECIFIED) {
+ return;
+ }
+
+ final Resources res = backgroundView.getResources();
+ final int frameWidth = res.getDimensionPixelSize(R.dimen.settingslib_illustration_width);
+ final int frameHeight = res.getDimensionPixelSize(R.dimen.settingslib_illustration_height);
+ final int restrictedMaxHeight = Math.min(mMaxHeight, frameHeight);
+ backgroundView.setMaxHeight(restrictedMaxHeight);
+ illustrationView.setMaxHeight(restrictedMaxHeight);
+
+ // Ensures the illustration view size is smaller than or equal to the background view size.
+ final float aspectRatio = (float) frameWidth / frameHeight;
+ illustrationView.setMaxWidth((int) (restrictedMaxHeight * aspectRatio));
+ }
+
private void startAnimation(Drawable drawable) {
if (!(drawable instanceof Animatable)) {
return;
diff --git a/packages/SettingsLib/SearchWidget/res/values-as/strings.xml b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
index d3f922a..8d95131 100644
--- a/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"সন্ধান সম্পৰ্কীয় ছেটিংসমূহ"</string>
+ <string name="search_menu" msgid="1914043873178389845">"সন্ধান সম্পৰ্কীয় ছেটিং"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 9f9b11d..656fe6a 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -155,7 +155,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"ব্যৱহাৰকাৰী: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"কিছুমান ডিফ\'ল্ট ছেট কৰা হৈছে"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"কোনো ডিফ\'ল্ট ছেট কৰা হোৱা নাই"</string>
- <string name="tts_settings" msgid="8130616705989351312">"পাঠৰ পৰা কথনৰ ছেটিংসমূহ"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"পাঠৰ পৰা কথনৰ ছেটিং"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"পাঠৰ পৰা কথনৰ আউটপুট"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"কথা কোৱাৰ হাৰ"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"পাঠ কথনৰ বেগ"</string>
@@ -177,8 +177,8 @@
<string name="tts_status_requires_network" msgid="8327617638884678896">"<xliff:g id="LOCALE">%1$s</xliff:g>ক নেটৱৰ্ক সংযোগৰ দৰকাৰ"</string>
<string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> সমৰ্থিত নহয়"</string>
<string name="tts_status_checking" msgid="8026559918948285013">"পৰীক্ষা কৰি থকা হৈছে…"</string>
- <string name="tts_engine_settings_title" msgid="7849477533103566291">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>ৰ বাবে ছেটিংসমূহ"</string>
- <string name="tts_engine_settings_button" msgid="477155276199968948">"ইঞ্জিনৰ ছেটিংসমূহ লঞ্চ কৰক"</string>
+ <string name="tts_engine_settings_title" msgid="7849477533103566291">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>ৰ ছেটিং"</string>
+ <string name="tts_engine_settings_button" msgid="477155276199968948">"ইঞ্জিনৰ ছেটিং লঞ্চ কৰক"</string>
<string name="tts_engine_preference_section_title" msgid="3861562305498624904">"অগ্ৰাধিকাৰপ্ৰাপ্ত ইঞ্জিন"</string>
<string name="tts_general_section_title" msgid="8919671529502364567">"সাধাৰণ"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"কথনভংগী তীব্ৰতা ৰিছেট কৰক"</string>
@@ -201,9 +201,9 @@
<string name="development_settings_enable" msgid="4285094651288242183">"বিকাশকৰ্তা বিষয়ক বিকল্পসমূহ সক্ষম কৰক"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"এপৰ বিকাশৰ বাবে বিকল্পসমূহ ছেট কৰক"</string>
<string name="development_settings_not_available" msgid="355070198089140951">"এইজন ব্যৱহাৰকাৰীৰ বাবে বিকাশকৰ্তাৰ বিকল্পসমূহ উপলব্ধ নহয়"</string>
- <string name="vpn_settings_not_available" msgid="2894137119965668920">"ভিপিএন ছেটিংসমূহ এই ব্যৱহাৰকাৰীজনৰ বাবে উপলব্ধ নহয়"</string>
- <string name="tethering_settings_not_available" msgid="266821736434699780">"এই ব্যৱহাৰকাৰীৰ বাবে টেডাৰিং ছেটিংসমূহ উপলব্ধ নহয়"</string>
- <string name="apn_settings_not_available" msgid="1147111671403342300">"এই ব্যৱহাৰকাৰীৰ বাবে একচেছ পইণ্টৰ নাম ছেটিংসমূহ উপলব্ধ নহয়"</string>
+ <string name="vpn_settings_not_available" msgid="2894137119965668920">"ভিপিএন ছেটিং এই ব্যৱহাৰকাৰীজনৰ বাবে উপলব্ধ নহয়"</string>
+ <string name="tethering_settings_not_available" msgid="266821736434699780">"এই ব্যৱহাৰকাৰীৰ বাবে টেডাৰিং ছেটিং উপলব্ধ নহয়"</string>
+ <string name="apn_settings_not_available" msgid="1147111671403342300">"এই ব্যৱহাৰকাৰীৰ বাবে এক্সেছ পইণ্টৰ নামৰ ছেটিং উপলব্ধ নহয়"</string>
<string name="enable_adb" msgid="8072776357237289039">"ইউএছবি ডিবাগিং"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"ইউএছবি সংযোগ হৈ থকাৰ অৱস্থাত ডিবাগ ম\'ড"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"ইউএছবি ডিবাগিং অনুমতিসমূহ প্ৰত্যাহাৰ কৰক"</string>
@@ -305,7 +305,7 @@
<string name="adbwifi_warning_title" msgid="727104571653031865">"ৱায়াৰলেচ ডি\'বাগিংৰ অনুমতি দিবনে?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"ৱায়াৰলেচ ডি\'বাগিং কেৱল বিকাশৰ উদ্দেশ্যেৰে কৰা হয়। আপোনাৰ কম্পিউটাৰ আৰু আপোনাৰ ডিভাইচৰ মাজত ডেটা প্ৰতিলিপি কৰিবলৈ, কোনো জাননী নিদিয়াকৈয়ে আপোনাৰ ডিভাইচত এপ্সমূহ ইনষ্টল কৰিবলৈ আৰু লগ ডেটা পঢ়িবলৈ এইটো ব্যৱহাৰ কৰক।"</string>
<string name="adb_keys_warning_message" msgid="2968555274488101220">"আপুনি আগতে ইউএছবি ডিবাগিঙৰ বাবে প্ৰৱেশৰ অনুমতি দিয়া সকলো কম্পিউটাৰৰ পৰা সেই অনুমতি প্ৰত্যাহাৰ কৰেনে?"</string>
- <string name="dev_settings_warning_title" msgid="8251234890169074553">"বিকাশৰ কামৰ বাবে থকা ছেটিংবিলাকক অনুমতি দিবনে?"</string>
+ <string name="dev_settings_warning_title" msgid="8251234890169074553">"বিকাশৰ কামৰ বাবে থকা ছেটিঙৰ অনুমতি দিবনে?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"এই ছেটিংসমূহ বিকাশৰ কামত ব্যৱহাৰ কৰিবলৈ তৈয়াৰ কৰা হৈছে। সেইবিলাকে আপোনাৰ ডিভাইচ আৰু তাত থকা এপ্লিকেশ্বনসমূহক অকামিলা কৰি পেলাব পাৰে আৰু সেইবিলাকৰ কাৰণে এপ্লিকেশ্বনসমূহে অদ্ভুত আচৰণ কৰিব পাৰে।"</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"ইউএছবিৰ যোগেৰে এপৰ সত্যাপন কৰক"</string>
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADTৰ যোগেৰে ইনষ্টল কৰা এপসমূহে কিবা ক্ষতিকাৰক আচৰণ কৰিছে নেকি পৰীক্ষা কৰক।"</string>
@@ -468,7 +468,7 @@
<string name="external_source_trusted" msgid="1146522036773132905">"অনুমতি দিয়া হৈছে"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"অনুমতি দিয়া হোৱা নাই"</string>
<string name="install_other_apps" msgid="3232595082023199454">"অজ্ঞাত এপ্ ইনষ্টল কৰক"</string>
- <string name="home" msgid="973834627243661438">"ছেটিংসমূহৰ গৃহপৃষ্ঠা"</string>
+ <string name="home" msgid="973834627243661438">"Settingsৰ গৃহপৃষ্ঠা"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"০%"</item>
<item msgid="8894873528875953317">"৫০%"</item>
@@ -488,7 +488,7 @@
<string name="retail_demo_reset_title" msgid="1866911701095959800">"পাছৱৰ্ড দৰকাৰী"</string>
<string name="active_input_method_subtypes" msgid="4232680535471633046">"সক্ৰিয়হৈ থকা ইনপুট পদ্ধতিসমূহ"</string>
<string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"ছিষ্টেমৰ ভাষা ব্যৱহাৰ কৰক"</string>
- <string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>ৰ ছেটিংবিলাক খুলিব পৰা নগ\'ল"</string>
+ <string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>ৰ ছেটিং খুলিব পৰা নগ\'ল"</string>
<string name="ime_security_warning" msgid="6547562217880551450">"এই ইনপুট পদ্ধতিটোৱে আপুনি টাইপ কৰা আপোনাৰ ব্যক্তিগত ডেটা যেনে পাছৱৰ্ডসমূহ আৰু ক্ৰেডিট কাৰ্ডৰ নম্বৰসমূহকে ধৰি সকলো পাঠ সংগ্ৰহ কৰিবলৈ সক্ষম হ\'ব পাৰে। <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> এপটোৰ লগত ই সংলগ্ন। এই ইনপুট পদ্ধতিটো ব্যৱহাৰ কৰেনে?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"টোকা: ৰিবুট কৰাৰ পিছত আপুনি ফ\'নটো আনলক নকৰালৈকে এই এপটো ষ্টাৰ্ট নহ’ব"</string>
<string name="ims_reg_title" msgid="8197592958123671062">"আইএমএছ পঞ্জীয়ন স্থিতি"</string>
@@ -546,7 +546,7 @@
<string name="user_add_user_item_title" msgid="2394272381086965029">"ব্যৱহাৰকাৰী"</string>
<string name="user_add_profile_item_title" msgid="3111051717414643029">"সীমিত প্ৰ\'ফাইল"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"নতুন ব্যৱহাৰকাৰী যোগ কৰিবনে?"</string>
- <string name="user_add_user_message_long" msgid="1527434966294733380">"আপুনি অতিৰিক্ত ব্য়ৱহাৰকাৰীক যোগ কৰি এই ডিভাইচটো অন্য় ব্য়ক্তিৰ সৈতে শ্বেয়াৰ কৰিব পাৰে। প্ৰতিজন ব্য়ৱহাৰকাৰীৰ বাবে নিজাকৈ ঠাই আছে যাক তেওঁলোকে এপ্, ৱালপেপাৰ আৰু অন্য়ান্য় বস্তুৰ বাবে নিজৰ উপযোগিতা অনুযায়ী ব্য়ৱহাৰ কৰিব পাৰে। ব্য়ৱহাৰকাৰীসকলে সকলোকে প্ৰভাৱান্বিত কৰা ৱাই-ফাইৰ নিচিনা ডিভাইচৰ ছেটিংসমূহ সাল-সলনি কৰিবও পাৰে।\n\nআপুনি যেতিয়া কোনো নতুন ব্য়ৱহাৰকাৰীক যোগ কৰে সেই ব্য়ক্তিজনে নিজেই নিজৰ বাবে ঠাই ছেট আপ কৰিব লাগিব।\n\nসকলো ব্য়ৱহাৰকাৰীএ অন্য় ব্য়ৱহাৰকাৰীৰ বাবে এপসমূহ আপডে’ট কৰিব পাৰে। সাধ্য় সুবিধাসমূহৰ ছেটিং আৰু সেৱাসমূহ নতুন ব্য়ৱহাৰকাৰীলৈ স্থানান্তৰ নহ\'বও পাৰে।"</string>
+ <string name="user_add_user_message_long" msgid="1527434966294733380">"আপুনি অতিৰিক্ত ব্য়ৱহাৰকাৰীক যোগ কৰি এই ডিভাইচটো অন্য় ব্য়ক্তিৰ সৈতে শ্বেয়াৰ কৰিব পাৰে। প্ৰতিজন ব্য়ৱহাৰকাৰীৰ বাবে নিজাকৈ ঠাই আছে যাক তেওঁলোকে এপ্, ৱালপেপাৰ আৰু অন্য়ান্য় বস্তুৰ বাবে নিজৰ উপযোগিতা অনুযায়ী ব্য়ৱহাৰ কৰিব পাৰে। ব্য়ৱহাৰকাৰীসকলে সকলোকে প্ৰভাৱান্বিত কৰা ৱাই-ফাইৰ নিচিনা ডিভাইচৰ ছেটিং সাল-সলনি কৰিবও পাৰে।\n\nআপুনি যেতিয়া কোনো নতুন ব্য়ৱহাৰকাৰীক যোগ কৰে সেই ব্য়ক্তিজনে নিজেই নিজৰ বাবে ঠাই ছেট আপ কৰিব লাগিব।\n\nসকলো ব্য়ৱহাৰকাৰীয়ে অন্য় ব্য়ৱহাৰকাৰীৰ বাবে এপ্সমূহ আপডে’ট কৰিব পাৰে। সাধ্য় সুবিধাসমূহৰ ছেটিং আৰু সেৱাসমূহ নতুন ব্য়ৱহাৰকাৰীলৈ স্থানান্তৰ নহ\'বও পাৰে।"</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"আপুনি যেতিয়া এজন নতুন ব্যৱহাৰকাৰী যোগ কৰে, তেওঁ নিজৰ ঠাই ছেট আপ কৰা প্ৰয়োজন।\n\nযিকোনো ব্যৱহাৰকাৰীয়ে সকলো ব্যৱহাৰকাৰীৰ বাবে এপ্ আপডেইট কৰিব পাৰে।"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"ব্যৱহাৰকাৰী এতিয়া ছেট আপ কৰিবনে?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"ডিভাইচটো লৈ নিজৰ ঠাই ছেটআপ কৰিবলৈ নতুন ব্যৱহাৰকাৰী উপলব্ধ থকাটো নিশ্চিত কৰক"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 5da56d1..6f8f99b 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -167,8 +167,8 @@
<string name="tts_default_lang_summary" msgid="9042620014800063470">"టెక్స్ట్ను చదివి వినిపించేటప్పుడు, ఒక్కో భాషకు వాడాల్సిన నిర్దిష్ట వాయిస్ను సెట్ చేస్తుంది"</string>
<string name="tts_play_example_title" msgid="1599468547216481684">"ఒక ఉదాహరణ వినండి"</string>
<string name="tts_play_example_summary" msgid="634044730710636383">"ప్రసంగ సమన్వయం గురించి సంక్షిప్త ప్రదర్శనను ప్లే చేయి"</string>
- <string name="tts_install_data_title" msgid="1829942496472751703">"వాయిస్ డేటాను ఇన్స్టాల్ చేయి"</string>
- <string name="tts_install_data_summary" msgid="3608874324992243851">"ప్రసంగ సమన్వయం కోసం అవసరమైన వాయిస్ డేటాను ఇన్స్టాల్ చేయండి"</string>
+ <string name="tts_install_data_title" msgid="1829942496472751703">"వాయిస్ డేటాను ఇన్స్టాల్ చేయండి"</string>
+ <string name="tts_install_data_summary" msgid="3608874324992243851">"స్పీచ్ సమన్వయం కోసం అవసరమైన వాయిస్ డేటాను ఇన్స్టాల్ చేయండి"</string>
<string name="tts_engine_security_warning" msgid="3372432853837988146">"ఈ ప్రసంగ సమన్వయ ఇంజిన్ చదివి వినిపించబడే మొత్తం వచనాన్ని అలాగే పాస్వర్డలు మరియు క్రెడిట్ కార్డు నంబర్ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ఇంజిన్లో అందించబడుతుంది. ఈ ప్రసంగ సమన్వయ ఇంజిన్ యొక్క వినియోగాన్ని ప్రారంభించాలా?"</string>
<string name="tts_engine_network_required" msgid="8722087649733906851">"వచనం నుండి ప్రసంగం అవుట్పుట్ కోసం ఈ భాషకు పని చేస్తున్న నెట్వర్క్ కనెక్షన్ కావాలి."</string>
<string name="tts_default_sample_string" msgid="6388016028292967973">"ఇది ప్రసంగ సమన్వయానికి ఉదాహరణ"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
index bc0c6f3..794b0eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib;
+import com.android.settingslib.mobile.TelephonyIcons;
+
import java.text.SimpleDateFormat;
import java.util.Objects;
@@ -40,9 +42,17 @@
// For logging.
public final String name;
- public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
- int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
- int discContentDesc) {
+ public IconGroup(
+ String name,
+ int[][] sbIcons,
+ int[][] qsIcons,
+ int[] contentDesc,
+ int sbNullState,
+ int qsNullState,
+ int sbDiscState,
+ int qsDiscState,
+ int discContentDesc
+ ) {
this.name = name;
this.sbIcons = sbIcons;
this.qsIcons = qsIcons;
@@ -131,6 +141,19 @@
&& other.activityOut == activityOut
&& other.rssi == rssi;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ connected,
+ enabled,
+ level,
+ inetCondition,
+ iconGroup,
+ activityIn,
+ activityOut,
+ rssi);
+ }
}
/**
@@ -139,18 +162,31 @@
public static class MobileIconGroup extends IconGroup {
public final int dataContentDescription; // mContentDescriptionDataType
public final int dataType;
- public final boolean isWide;
- public final int qsDataType;
- public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
- int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
- int discContentDesc, int dataContentDesc, int dataType, boolean isWide) {
- super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
- qsDiscState, discContentDesc);
+ public MobileIconGroup(
+ String name,
+ int[][] sbIcons,
+ int[][] qsIcons,
+ int[] contentDesc,
+ int sbNullState,
+ int qsNullState,
+ int sbDiscState,
+ int qsDiscState,
+ int discContentDesc,
+ int dataContentDesc,
+ int dataType
+ ) {
+ super(name,
+ sbIcons,
+ qsIcons,
+ contentDesc,
+ sbNullState,
+ qsNullState,
+ sbDiscState,
+ qsDiscState,
+ discContentDesc);
this.dataContentDescription = dataContentDesc;
this.dataType = dataType;
- this.isWide = isWide;
- this.qsDataType = dataType; // TODO: remove this field
}
}
@@ -187,6 +223,27 @@
defaultDataOff = state.defaultDataOff;
}
+ /** @return true if this state is disabled or not default data */
+ public boolean isDataDisabledOrNotDefault() {
+ return (iconGroup == TelephonyIcons.DATA_DISABLED
+ || (iconGroup == TelephonyIcons.NOT_DEFAULT_DATA)) && userSetup;
+ }
+
+ /** @return if this state is considered to have inbound activity */
+ public boolean hasActivityIn() {
+ return dataConnected && !carrierNetworkChangeMode && activityIn;
+ }
+
+ /** @return if this state is considered to have outbound activity */
+ public boolean hasActivityOut() {
+ return dataConnected && !carrierNetworkChangeMode && activityOut;
+ }
+
+ /** @return true if this state should show a RAT icon in quick settings */
+ public boolean showQuickSettingsRatIcon() {
+ return dataConnected || isDataDisabledOrNotDefault();
+ }
+
@Override
protected void toString(StringBuilder builder) {
super.toString(builder);
@@ -202,23 +259,40 @@
builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode)
.append(',');
builder.append("userSetup=").append(userSetup).append(',');
- builder.append("defaultDataOff=").append(defaultDataOff);
+ builder.append("defaultDataOff=").append(defaultDataOff).append(',');
+ builder.append("showQuickSettingsRatIcon=").append(showQuickSettingsRatIcon());
}
@Override
public boolean equals(Object o) {
return super.equals(o)
- && Objects.equals(((MobileState) o).networkName, networkName)
- && Objects.equals(((MobileState) o).networkNameData, networkNameData)
- && ((MobileState) o).dataSim == dataSim
- && ((MobileState) o).dataConnected == dataConnected
- && ((MobileState) o).isEmergency == isEmergency
- && ((MobileState) o).airplaneMode == airplaneMode
- && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
- && ((MobileState) o).userSetup == userSetup
- && ((MobileState) o).isDefault == isDefault
- && ((MobileState) o).roaming == roaming
- && ((MobileState) o).defaultDataOff == defaultDataOff;
+ && Objects.equals(((MobileState) o).networkName, networkName)
+ && Objects.equals(((MobileState) o).networkNameData, networkNameData)
+ && ((MobileState) o).dataSim == dataSim
+ && ((MobileState) o).dataConnected == dataConnected
+ && ((MobileState) o).isEmergency == isEmergency
+ && ((MobileState) o).airplaneMode == airplaneMode
+ && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
+ && ((MobileState) o).userSetup == userSetup
+ && ((MobileState) o).isDefault == isDefault
+ && ((MobileState) o).roaming == roaming
+ && ((MobileState) o).defaultDataOff == defaultDataOff;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(),
+ networkName,
+ networkNameData,
+ dataSim,
+ dataConnected,
+ isEmergency,
+ airplaneMode,
+ carrierNetworkChangeMode,
+ userSetup,
+ isDefault,
+ roaming,
+ defaultDataOff);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
index a0c8663..ea5105b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
@@ -319,23 +319,11 @@
@Override
public boolean onPreferenceClick(Preference preference) {
- // Activity to start if they click on the preference. Must start in new task to ensure
- // that "android.settings.LOCATION_SOURCE_SETTINGS" brings user back to
- // Settings > Location.
+ // Activity to start if they click on the preference.
Intent settingIntent = new Intent();
settingIntent.setClassName(mInfo.packageName, mInfo.settingsActivity);
+ // No flags set to ensure the activity is launched within the same settings task.
logPreferenceClick(settingIntent);
- // Sometimes the user may navigate back to "Settings" and launch another different
- // injected setting after one injected setting has been launched.
- //
- // FLAG_ACTIVITY_CLEAR_TOP allows multiple Activities to stack on each other. When
- // "back" button is clicked, the user will navigate through all the injected settings
- // launched before. Such behavior could be quite confusing sometimes.
- //
- // In order to avoid such confusion, we use FLAG_ACTIVITY_CLEAR_TASK, which always clear
- // up all existing injected settings and make sure that "back" button always brings the
- // user back to "Settings" directly.
- settingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivityAsUser(settingIntent, mInfo.mUserHandle);
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index f8565bc..d4e58f7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -50,178 +50,194 @@
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.carrier_network_change_mode,
- 0,
- false);
+ 0
+ );
public static final MobileIconGroup THREE_G = new MobileIconGroup(
"3G",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3g,
- TelephonyIcons.ICON_3G,
- true);
+ TelephonyIcons.ICON_3G
+ );
public static final MobileIconGroup WFC = new MobileIconGroup(
"WFC",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- 0, 0, false);
+ 0,
+ 0);
public static final MobileIconGroup UNKNOWN = new MobileIconGroup(
"Unknown",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- 0, 0, false);
+ 0,
+ 0);
public static final MobileIconGroup E = new MobileIconGroup(
"E",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_edge,
- TelephonyIcons.ICON_E,
- false);
+ TelephonyIcons.ICON_E
+ );
public static final MobileIconGroup ONE_X = new MobileIconGroup(
"1X",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_cdma,
- TelephonyIcons.ICON_1X,
- true);
+ TelephonyIcons.ICON_1X
+ );
public static final MobileIconGroup G = new MobileIconGroup(
"G",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_gprs,
- TelephonyIcons.ICON_G,
- false);
+ TelephonyIcons.ICON_G
+ );
public static final MobileIconGroup H = new MobileIconGroup(
"H",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3_5g,
- TelephonyIcons.ICON_H,
- false);
+ TelephonyIcons.ICON_H
+ );
public static final MobileIconGroup H_PLUS = new MobileIconGroup(
"H+",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3_5g_plus,
- TelephonyIcons.ICON_H_PLUS,
- false);
+ TelephonyIcons.ICON_H_PLUS
+ );
public static final MobileIconGroup FOUR_G = new MobileIconGroup(
"4G",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_4g,
- TelephonyIcons.ICON_4G,
- true);
+ TelephonyIcons.ICON_4G
+ );
public static final MobileIconGroup FOUR_G_PLUS = new MobileIconGroup(
"4G+",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_4g_plus,
- TelephonyIcons.ICON_4G_PLUS,
- true);
+ TelephonyIcons.ICON_4G_PLUS
+ );
public static final MobileIconGroup LTE = new MobileIconGroup(
"LTE",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_lte,
- TelephonyIcons.ICON_LTE,
- true);
+ TelephonyIcons.ICON_LTE
+ );
public static final MobileIconGroup LTE_PLUS = new MobileIconGroup(
"LTE+",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_lte_plus,
- TelephonyIcons.ICON_LTE_PLUS,
- true);
+ TelephonyIcons.ICON_LTE_PLUS
+ );
public static final MobileIconGroup LTE_CA_5G_E = new MobileIconGroup(
"5Ge",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5ge_html,
- TelephonyIcons.ICON_5G_E,
- true);
+ TelephonyIcons.ICON_5G_E
+ );
public static final MobileIconGroup NR_5G = new MobileIconGroup(
"5G",
@@ -234,8 +250,8 @@
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5g,
- TelephonyIcons.ICON_5G,
- true);
+ TelephonyIcons.ICON_5G
+ );
public static final MobileIconGroup NR_5G_PLUS = new MobileIconGroup(
"5G_PLUS",
@@ -248,34 +264,36 @@
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5g_plus,
- TelephonyIcons.ICON_5G_PLUS,
- true);
+ TelephonyIcons.ICON_5G_PLUS
+ );
public static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
"DataDisabled",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.cell_data_off_content_description,
- 0,
- false);
+ 0
+ );
public static final MobileIconGroup NOT_DEFAULT_DATA = new MobileIconGroup(
"NotDefaultData",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.not_default_data_content_description,
- 0,
- false);
+ 0
+ );
public static final MobileIconGroup CARRIER_MERGED_WIFI = new MobileIconGroup(
"CWF",
@@ -288,8 +306,8 @@
/* qsDiscState= */ 0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_carrier_wifi,
- TelephonyIcons.ICON_CWF,
- /* isWide= */ true);
+ TelephonyIcons.ICON_CWF
+ );
// When adding a new MobileIconGround, check if the dataContentDescription has to be filtered
// in QSCarrier#hasValidTypeContentDescription
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index b65637f..5ee919b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.text.MessageFormat;
import android.icu.text.RelativeDateTimeFormatter;
import android.icu.text.RelativeDateTimeFormatter.RelativeUnit;
import android.icu.util.Measure;
@@ -31,7 +32,9 @@
import com.android.settingslib.R;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Locale;
+import java.util.Map;
/** Utility class for generally useful string methods **/
public class StringUtil {
@@ -183,4 +186,37 @@
return formatRelativeTime(context, millis, withSeconds,
RelativeDateTimeFormatter.Style.LONG);
}
+
+ /**
+ * Get ICU plural string without additional arguments
+ *
+ * @param context Context used to get the string
+ * @param count The number used to get the correct string for the current language's plural
+ * rules.
+ * @param resId Resource id of the string
+ *
+ * @return Formatted plural string
+ */
+ public static String getIcuPluralsString(Context context, int count, int resId) {
+ MessageFormat msgFormat = new MessageFormat(context.getResources().getString(resId),
+ Locale.getDefault());
+ Map<String, Object> arguments = new HashMap<>();
+ arguments.put("count", count);
+ return msgFormat.format(arguments);
+ }
+
+ /**
+ * Get ICU plural string with additional arguments
+ *
+ * @param context Context used to get the string
+ * @param args String arguments
+ * @param resId Resource id of the string
+ *
+ * @return Formatted plural string
+ */
+ public static String getIcuPluralsString(Context context, Map<String, Object> args, int resId) {
+ MessageFormat msgFormat = new MessageFormat(context.getResources().getString(resId),
+ Locale.getDefault());
+ return msgFormat.format(args);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java
new file mode 100644
index 0000000..92a32bc
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import com.android.settingslib.mobile.TelephonyIcons;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class MobileStateTest {
+
+ private SignalIcon.MobileState mState = new SignalIcon.MobileState();
+
+ @Before
+ public void setUp() {
+ }
+
+ @Test
+ public void testIsDataDisabledOrNotDefault_dataDisabled() {
+ mState.iconGroup = TelephonyIcons.DATA_DISABLED;
+ mState.userSetup = true;
+
+ assertTrue(mState.isDataDisabledOrNotDefault());
+ }
+
+ @Test
+ public void testIsDataDisabledOrNotDefault_notDefaultData() {
+ mState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
+ mState.userSetup = true;
+
+ assertTrue(mState.isDataDisabledOrNotDefault());
+ }
+
+ @Test
+ public void testIsDataDisabledOrNotDefault_notDisabled() {
+ mState.iconGroup = TelephonyIcons.G;
+ mState.userSetup = true;
+
+ assertFalse(mState.isDataDisabledOrNotDefault());
+ }
+
+ @Test
+ public void testHasActivityIn_noData_noActivity() {
+ mState.dataConnected = false;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityIn = false;
+
+ assertFalse(mState.hasActivityIn());
+ }
+
+ @Test
+ public void testHasActivityIn_noData_activityIn() {
+ mState.dataConnected = false;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityIn = true;
+
+ assertFalse(mState.hasActivityIn());
+ }
+
+ @Test
+ public void testHasActivityIn_dataConnected_activityIn() {
+ mState.dataConnected = true;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityIn = true;
+
+ assertTrue(mState.hasActivityIn());
+ }
+
+ @Test
+ public void testHasActivityIn_carrierNetworkChange() {
+ mState.dataConnected = true;
+ mState.carrierNetworkChangeMode = true;
+ mState.activityIn = true;
+
+ assertFalse(mState.hasActivityIn());
+ }
+
+ @Test
+ public void testHasActivityOut_noData_noActivity() {
+ mState.dataConnected = false;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityOut = false;
+
+ assertFalse(mState.hasActivityOut());
+ }
+
+ @Test
+ public void testHasActivityOut_noData_activityOut() {
+ mState.dataConnected = false;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityOut = true;
+
+ assertFalse(mState.hasActivityOut());
+ }
+
+ @Test
+ public void testHasActivityOut_dataConnected_activityOut() {
+ mState.dataConnected = true;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityOut = true;
+
+ assertTrue(mState.hasActivityOut());
+ }
+
+ @Test
+ public void testHasActivityOut_carrierNetworkChange() {
+ mState.dataConnected = true;
+ mState.carrierNetworkChangeMode = true;
+ mState.activityOut = true;
+
+ assertFalse(mState.hasActivityOut());
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index 9e3312a..29549d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -55,6 +55,7 @@
@Mock
private ViewGroup mRootView;
private Uri mImageUri;
+ private ImageView mBackgroundView;
private LottieAnimationView mAnimationView;
private IllustrationPreference mPreference;
private PreferenceViewHolder mViewHolder;
@@ -66,6 +67,7 @@
MockitoAnnotations.initMocks(this);
mImageUri = new Uri.Builder().build();
+ mBackgroundView = new ImageView(mContext);
mAnimationView = spy(new LottieAnimationView(mContext));
mMiddleGroundLayout = new FrameLayout(mContext);
final FrameLayout illustrationFrame = new FrameLayout(mContext);
@@ -73,6 +75,7 @@
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
doReturn(mMiddleGroundLayout).when(mRootView).findViewById(R.id.middleground_layout);
+ doReturn(mBackgroundView).when(mRootView).findViewById(R.id.background_view);
doReturn(mAnimationView).when(mRootView).findViewById(R.id.lottie_view);
doReturn(illustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(mRootView));
@@ -155,4 +158,32 @@
verify(mAnimationView).setFailureListener(any());
}
+
+ @Test
+ public void setMaxHeight_smallerThanRestrictedHeight_matchResult() {
+ final int restrictedHeight =
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.settingslib_illustration_height);
+ final int maxHeight = restrictedHeight - 200;
+
+ mPreference.setMaxHeight(maxHeight);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mBackgroundView.getMaxHeight()).isEqualTo(maxHeight);
+ assertThat(mAnimationView.getMaxHeight()).isEqualTo(maxHeight);
+ }
+
+ @Test
+ public void setMaxHeight_largerThanRestrictedHeight_specificHeight() {
+ final int restrictedHeight =
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.settingslib_illustration_height);
+ final int maxHeight = restrictedHeight + 200;
+
+ mPreference.setMaxHeight(maxHeight);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mBackgroundView.getMaxHeight()).isEqualTo(restrictedHeight);
+ assertThat(mAnimationView.getMaxHeight()).isEqualTo(restrictedHeight);
+ }
}
diff --git a/packages/SettingsProvider/res/values-as/strings.xml b/packages/SettingsProvider/res/values-as/strings.xml
index 89b7c1e..ead9f4d 100644
--- a/packages/SettingsProvider/res/values-as/strings.xml
+++ b/packages/SettingsProvider/res/values-as/strings.xml
@@ -19,7 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4567566098528588863">"ছেটিংছসমূহৰ সঞ্চয়াগাৰ"</string>
- <string name="wifi_softap_config_change" msgid="5688373762357941645">"হটস্পটৰ ছেটিংসমূহ সলনি হৈছে"</string>
+ <string name="app_label" msgid="4567566098528588863">"ছেটিঙৰ ষ্ট\'ৰেজ"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"হটস্পটৰ ছেটিং সলনি হৈছে"</string>
<string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"সবিশেষ চাবলৈ টিপক"</string>
</resources>
diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml b/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml
new file mode 100644
index 0000000..c6b87d3
--- /dev/null
+++ b/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- The enter animation of the host dialog is a translation of 0px that lasts 500ms so that the -->
+<!-- host dialog is directly visible but the dim background still takes 500ms to fade in. -->
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0"
+ android:toXDelta="0"
+ android:duration="500" />
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml b/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml
new file mode 100644
index 0000000..a0f441e
--- /dev/null
+++ b/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<alpha
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_cubic"
+ android:duration="150"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0" />
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/values/ids.xml b/packages/SystemUI/animation/res/values/ids.xml
new file mode 100644
index 0000000..ef60a24
--- /dev/null
+++ b/packages/SystemUI/animation/res/values/ids.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <item type="id" name="launch_animation_running"/>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/values/styles.xml b/packages/SystemUI/animation/res/values/styles.xml
new file mode 100644
index 0000000..ad06c91
--- /dev/null
+++ b/packages/SystemUI/animation/res/values/styles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <style name="HostDialogTheme">
+ <item name="android:windowAnimationStyle">@style/Animation.HostDialog</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ </style>
+
+ <style name="Animation.HostDialog" parent="@android:style/Animation">
+ <item name="android:windowEnterAnimation">@anim/launch_host_dialog_enter</item>
+ <item name="android:windowExitAnimation">@anim/launch_host_dialog_exit</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 9c1e129..7020603 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -1,23 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.animation
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.animation.ValueAnimator
import android.app.ActivityManager
import android.app.ActivityTaskManager
import android.app.PendingIntent
import android.app.TaskInfo
-import android.content.Context
import android.graphics.Matrix
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.graphics.RectF
-import android.graphics.drawable.GradientDrawable
import android.os.Looper
import android.os.RemoteException
import android.util.Log
-import android.util.MathUtils
import android.view.IRemoteAnimationFinishedCallback
import android.view.IRemoteAnimationRunner
import android.view.RemoteAnimationAdapter
@@ -26,7 +34,6 @@
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
-import android.view.animation.AnimationUtils
import android.view.animation.PathInterpolator
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
@@ -38,52 +45,23 @@
* A class that allows activities to be started in a seamless way from a view that is transforming
* nicely into the starting window.
*/
-class ActivityLaunchAnimator(
- private val callback: Callback,
- context: Context
-) {
+class ActivityLaunchAnimator(private val launchAnimator: LaunchAnimator) {
companion object {
- private const val DEBUG = false
- const val ANIMATION_DURATION = 500L
- private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
- private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
- private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
private const val ANIMATION_DELAY_NAV_FADE_IN =
- ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
+ LaunchAnimator.ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
private const val LAUNCH_TIMEOUT = 1000L
- @JvmField val CONTENT_FADE_OUT_INTERPOLATOR = PathInterpolator(0f, 0f, 0.2f, 1f)
- private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f)
private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
-
- private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
-
- /**
- * Given the [linearProgress] of a launch animation, return the linear progress of the
- * sub-animation starting [delay] ms after the launch animation and that lasts [duration].
- */
- @JvmStatic
- fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
- return MathUtils.constrain(
- (linearProgress * ANIMATION_DURATION - delay) / duration,
- 0.0f,
- 1.0f
- )
- }
}
- /** The interpolator used for the width, height, Y position and corner radius. */
- private val animationInterpolator = AnimationUtils.loadInterpolator(context,
- R.interpolator.launch_animation_interpolator_y)
-
- /** The interpolator used for the X position. */
- private val animationInterpolatorX = AnimationUtils.loadInterpolator(context,
- R.interpolator.launch_animation_interpolator_x)
-
- private val cornerRadii = FloatArray(8)
+ /**
+ * The callback of this animator. This should be set before any call to
+ * [start(Pending)IntentWithAnimation].
+ */
+ var callback: Callback? = null
/**
* Start an intent and animate the opening window. The intent will be started by running
@@ -119,6 +97,8 @@
return
}
+ val callback = this.callback ?: throw IllegalStateException(
+ "ActivityLaunchAnimator.callback must be set before using this animator")
val runner = Runner(controller)
val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
@@ -126,9 +106,9 @@
// keyguard with the animation
val animationAdapter = if (!hideKeyguardWithAnimation) {
RemoteAnimationAdapter(
- runner,
- ANIMATION_DURATION,
- ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
+ runner,
+ LaunchAnimator.ANIMATION_DURATION,
+ LaunchAnimator.ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
)
} else {
null
@@ -150,10 +130,10 @@
// Only animate if the app is not already on top and will be opened, unless we are on the
// keyguard.
val willAnimate =
- launchResult == ActivityManager.START_TASK_TO_FRONT ||
- launchResult == ActivityManager.START_SUCCESS ||
- (launchResult == ActivityManager.START_DELIVERED_TO_TOP &&
- hideKeyguardWithAnimation)
+ launchResult == ActivityManager.START_TASK_TO_FRONT ||
+ launchResult == ActivityManager.START_SUCCESS ||
+ (launchResult == ActivityManager.START_DELIVERED_TO_TOP &&
+ hideKeyguardWithAnimation)
Log.i(TAG, "launchResult=$launchResult willAnimate=$willAnimate " +
"hideKeyguardWithAnimation=$hideKeyguardWithAnimation")
@@ -234,7 +214,7 @@
*
* Note that all callbacks (onXXX methods) are all called on the main thread.
*/
- interface Controller {
+ interface Controller : LaunchAnimator.Controller {
companion object {
/**
* Return a [Controller] that will animate and expand [view] into the opening window.
@@ -259,53 +239,12 @@
}
/**
- * The container in which the view that started the intent will be animating together with
- * the opening window.
- *
- * This will be used to:
- * - Get the associated [Context].
- * - Compute whether we are expanding fully above the current window.
- * - Apply surface transactions in sync with RenderThread.
- *
- * This container can be changed to force this [Controller] to animate the expanding view
- * inside a different location, for instance to ensure correct layering during the
- * animation.
- */
- var launchContainer: ViewGroup
-
- /**
- * Return the [State] of the view that will be animated. We will animate from this state to
- * the final window state.
- *
- * Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the
- * animation.
- */
- fun createAnimatorState(): State
-
- /**
* The intent was started. If [willAnimate] is false, nothing else will happen and the
* animation will not be started.
*/
fun onIntentStarted(willAnimate: Boolean) {}
/**
- * The animation started. This is typically used to initialize any additional resource
- * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
- * fully above the [root view][getRootView].
- */
- fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
-
- /** The animation made progress and the expandable view [state] should be updated. */
- fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
-
- /**
- * The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was
- * called previously. This is typically used to clean up the resources initialized when the
- * animation was started.
- */
- fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
-
- /**
* The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after
* this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
* before the cancellation.
@@ -313,66 +252,11 @@
fun onLaunchAnimationCancelled() {}
}
- /** The state of an expandable view during an [ActivityLaunchAnimator] animation. */
- open class State(
- /** The position of the view in screen space coordinates. */
- var top: Int,
- var bottom: Int,
- var left: Int,
- var right: Int,
-
- var topCornerRadius: Float = 0f,
- var bottomCornerRadius: Float = 0f
- ) {
- private val startTop = top
- private val startBottom = bottom
- private val startLeft = left
- private val startRight = right
- private val startWidth = width
- private val startHeight = height
- val startCenterX = centerX
- val startCenterY = centerY
-
- val width: Int
- get() = right - left
-
- val height: Int
- get() = bottom - top
-
- open val topChange: Int
- get() = top - startTop
-
- open val bottomChange: Int
- get() = bottom - startBottom
-
- val leftChange: Int
- get() = left - startLeft
-
- val rightChange: Int
- get() = right - startRight
-
- val widthRatio: Float
- get() = width.toFloat() / startWidth
-
- val heightRatio: Float
- get() = height.toFloat() / startHeight
-
- val centerX: Float
- get() = left + width / 2f
-
- val centerY: Float
- get() = top + height / 2f
-
- /** Whether the expanded view should be visible or hidden. */
- var visible: Boolean = true
- }
-
@VisibleForTesting
inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
private val launchContainer = controller.launchContainer
private val context = launchContainer.context
private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer)
- private var animator: ValueAnimator? = null
private val matrix = Matrix()
private val invertMatrix = Matrix()
@@ -380,6 +264,7 @@
private var windowCropF = RectF()
private var timedOut = false
private var cancelled = false
+ private var animation: LaunchAnimator.Animation? = null
// A timeout to cancel the remote animation if it is not started within X milliseconds after
// the intent was started.
@@ -429,7 +314,7 @@
nonApps: Array<out RemoteAnimationTarget>?,
iCallback: IRemoteAnimationFinishedCallback?
) {
- if (DEBUG) {
+ if (LaunchAnimator.DEBUG) {
Log.d(TAG, "Remote animation started")
}
@@ -449,36 +334,20 @@
it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
}
- // Start state.
- val state = controller.createAnimatorState()
-
- val startTop = state.top
- val startBottom = state.bottom
- val startLeft = state.left
- val startRight = state.right
- val startXCenter = (startLeft + startRight) / 2f
- val startWidth = startRight - startLeft
-
- val startTopCornerRadius = state.topCornerRadius
- val startBottomCornerRadius = state.bottomCornerRadius
-
- // End state.
val windowBounds = window.screenSpaceBounds
- val endTop = windowBounds.top
- val endBottom = windowBounds.bottom
- val endLeft = windowBounds.left
- val endRight = windowBounds.right
- val endXCenter = (endLeft + endRight) / 2f
- val endWidth = endRight - endLeft
+ val endState = LaunchAnimator.State(
+ top = windowBounds.top,
+ bottom = windowBounds.bottom,
+ left = windowBounds.left,
+ right = windowBounds.right
+ )
+ val callback = this@ActivityLaunchAnimator.callback!!
+ val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo)
- // TODO(b/184121838): Ensure that we are launching on the same screen.
- val rootViewLocation = launchContainer.locationOnScreen
- val isExpandingFullyAbove = endTop <= rootViewLocation[1] &&
- endBottom >= rootViewLocation[1] + launchContainer.height &&
- endLeft <= rootViewLocation[0] &&
- endRight >= rootViewLocation[0] + launchContainer.width
-
- // TODO(b/184121838): We should somehow get the top and bottom radius of the window.
+ // TODO(b/184121838): We should somehow get the top and bottom radius of the window
+ // instead of recomputing isExpandingFullyAbove here.
+ val isExpandingFullyAbove =
+ launchAnimator.isExpandingFullyAbove(controller.launchContainer, endState)
val endRadius = if (isExpandingFullyAbove) {
// Most of the time, expanding fully above the root view means expanding in full
// screen.
@@ -488,97 +357,40 @@
// a radius of 0.
0f
}
+ endState.topCornerRadius = endRadius
+ endState.bottomCornerRadius = endRadius
- // We add an extra layer with the same color as the app splash screen background color,
- // which is usually the same color of the app background. We first fade in this layer
- // to hide the expanding view, then we fade it out with SRC mode to draw a hole in the
- // launch container and reveal the opening window.
- val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo)
- val windowBackgroundLayer = GradientDrawable().apply {
- setColor(windowBackgroundColor)
- alpha = 0
- }
-
- // Update state.
- val animator = ValueAnimator.ofFloat(0f, 1f)
- this.animator = animator
- animator.duration = ANIMATION_DURATION
- animator.interpolator = Interpolators.LINEAR
-
- val launchContainerOverlay = launchContainer.overlay
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
- if (DEBUG) {
- Log.d(TAG, "Animation started")
- }
-
+ // We animate the opening window and delegate the view expansion to [this.controller].
+ val delegate = this.controller
+ val controller = object : LaunchAnimator.Controller by delegate {
+ override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
callback.setBlursDisabledForAppLaunch(true)
- controller.onLaunchAnimationStart(isExpandingFullyAbove)
-
- // Add the drawable to the launch container overlay. Overlays always draw
- // drawables after views, so we know that it will be drawn above any view added
- // by the controller.
- launchContainerOverlay.add(windowBackgroundLayer)
+ delegate.onLaunchAnimationStart(isExpandingFullyAbove)
}
- override fun onAnimationEnd(animation: Animator?) {
- if (DEBUG) {
- Log.d(TAG, "Animation ended")
- }
-
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
callback.setBlursDisabledForAppLaunch(false)
iCallback?.invoke()
- controller.onLaunchAnimationEnd(isExpandingFullyAbove)
- launchContainerOverlay.remove(windowBackgroundLayer)
- }
- })
-
- animator.addUpdateListener { animation ->
- if (cancelled) {
- return@addUpdateListener
+ delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
}
- val linearProgress = animation.animatedFraction
- val progress = animationInterpolator.getInterpolation(linearProgress)
- val xProgress = animationInterpolatorX.getInterpolation(linearProgress)
- val xCenter = MathUtils.lerp(startXCenter, endXCenter, xProgress)
- val halfWidth = lerp(startWidth, endWidth, progress) / 2
-
- state.top = lerp(startTop, endTop, progress).roundToInt()
- state.bottom = lerp(startBottom, endBottom, progress).roundToInt()
- state.left = (xCenter - halfWidth).roundToInt()
- state.right = (xCenter + halfWidth).roundToInt()
-
- state.topCornerRadius = MathUtils.lerp(startTopCornerRadius, endRadius, progress)
- state.bottomCornerRadius =
- MathUtils.lerp(startBottomCornerRadius, endRadius, progress)
-
- // The expanding view can/should be hidden once it is completely coverred by the
- // windowBackgroundLayer.
- state.visible =
- getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1
-
- applyStateToWindow(window, state)
- applyStateToWindowBackgroundLayer(windowBackgroundLayer, state, linearProgress)
- navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
-
- // If we started expanding the view, we make it 1 pixel smaller on all sides to
- // avoid artefacts on the corners caused by anti-aliasing of the view background and
- // the window background layer.
- if (state.top != startTop && state.left != startLeft &&
- state.bottom != startBottom && state.right != startRight) {
- state.top += 1
- state.left += 1
- state.right -= 1
- state.bottom -= 1
+ override fun onLaunchAnimationProgress(
+ state: LaunchAnimator.State,
+ progress: Float,
+ linearProgress: Float
+ ) {
+ applyStateToWindow(window, state)
+ navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+ delegate.onLaunchAnimationProgress(state, progress, linearProgress)
}
- controller.onLaunchAnimationProgress(state, progress, linearProgress)
}
- animator.start()
+ // We draw a hole when the additional layer is fading out to reveal the opening window.
+ animation = launchAnimator.startAnimation(
+ controller, endState, windowBackgroundColor, drawHole = true)
}
- private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) {
+ private fun applyStateToWindow(window: RemoteAnimationTarget, state: LaunchAnimator.State) {
val screenBounds = window.screenSpaceBounds
val centerX = (screenBounds.left + screenBounds.right) / 2f
val centerY = (screenBounds.top + screenBounds.bottom) / 2f
@@ -632,48 +444,13 @@
transactionApplier.scheduleApply(params)
}
- private fun applyStateToWindowBackgroundLayer(
- drawable: GradientDrawable,
- state: State,
- linearProgress: Float
- ) {
- // Update position.
- drawable.setBounds(state.left, state.top, state.right, state.bottom)
-
- // Update radius.
- cornerRadii[0] = state.topCornerRadius
- cornerRadii[1] = state.topCornerRadius
- cornerRadii[2] = state.topCornerRadius
- cornerRadii[3] = state.topCornerRadius
- cornerRadii[4] = state.bottomCornerRadius
- cornerRadii[5] = state.bottomCornerRadius
- cornerRadii[6] = state.bottomCornerRadius
- cornerRadii[7] = state.bottomCornerRadius
- drawable.cornerRadii = cornerRadii
-
- // We first fade in the background layer to hide the expanding view, then fade it out
- // with SRC mode to draw a hole punch in the status bar and reveal the opening window.
- val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT)
- if (fadeInProgress < 1) {
- val alpha = CONTENT_FADE_OUT_INTERPOLATOR.getInterpolation(fadeInProgress)
- drawable.alpha = (alpha * 0xFF).roundToInt()
- drawable.setXfermode(null)
- } else {
- val fadeOutProgress = getProgress(linearProgress,
- ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
- val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress)
- drawable.alpha = (alpha * 0xFF).roundToInt()
- drawable.setXfermode(SRC_MODE)
- }
- }
-
private fun applyStateToNavigationBar(
navigationBar: RemoteAnimationTarget,
- state: State,
+ state: LaunchAnimator.State,
linearProgress: Float
) {
- val fadeInProgress = getProgress(linearProgress, ANIMATION_DELAY_NAV_FADE_IN,
- ANIMATION_DURATION_NAV_FADE_OUT)
+ val fadeInProgress = LaunchAnimator.getProgress(linearProgress,
+ ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT)
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
if (fadeInProgress > 0) {
@@ -682,13 +459,13 @@
0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat())
windowCrop.set(state.left, 0, state.right, state.height)
params
- .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
- .withMatrix(matrix)
- .withWindowCrop(windowCrop)
- .withVisibility(true)
+ .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
+ .withMatrix(matrix)
+ .withWindowCrop(windowCrop)
+ .withVisibility(true)
} else {
- val fadeOutProgress = getProgress(linearProgress, 0,
- ANIMATION_DURATION_NAV_FADE_OUT)
+ val fadeOutProgress = LaunchAnimator.getProgress(linearProgress, 0,
+ ANIMATION_DURATION_NAV_FADE_OUT)
params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress))
}
@@ -714,7 +491,7 @@
cancelled = true
removeTimeout()
context.mainExecutor.execute {
- animator?.cancel()
+ animation?.cancel()
controller.onLaunchAnimationCancelled()
}
}
@@ -726,9 +503,5 @@
e.printStackTrace()
}
}
-
- private fun lerp(start: Int, stop: Int, amount: Float): Float {
- return MathUtils.lerp(start.toFloat(), stop.toFloat(), amount)
- }
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
index d4be253..258ca6b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.animation
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
new file mode 100644
index 0000000..c2b3608
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+import android.app.Dialog
+import android.content.Context
+import android.graphics.Color
+import android.os.Looper
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.view.WindowManager
+import android.widget.FrameLayout
+
+private const val TAG = "DialogLaunchAnimator"
+
+/**
+ * A class that allows dialogs to be started in a seamless way from a view that is transforming
+ * nicely into the starting dialog.
+ *
+ * Important: Don't forget to call [DialogLaunchAnimator.onDozeAmountChanged] when the doze amount
+ * changes to gracefully handle dialogs fading out when the device is dozing.
+ */
+class DialogLaunchAnimator(
+ private val context: Context,
+ private val launchAnimator: LaunchAnimator,
+ private val hostDialogProvider: HostDialogProvider
+) {
+ private companion object {
+ private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.launch_animation_running
+ }
+
+ // TODO(b/201264644): Remove this set.
+ private val currentAnimations = hashSetOf<DialogLaunchAnimation>()
+
+ /**
+ * Show [dialog] by expanding it from [view].
+ *
+ * Caveats: When calling this function, the dialog content view will actually be stolen and
+ * attached to a different dialog (and thus a different window) which means that the actual
+ * dialog window will never be drawn. Moreover, unless [dialog] is a [ListenableDialog], you
+ * must call dismiss(), hide() and show() on the [Dialog] returned by this function to actually
+ * dismiss, hide or show the dialog.
+ */
+ fun showFromView(dialog: Dialog, view: View): Dialog {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw IllegalStateException(
+ "showFromView must be called from the main thread and dialog must be created in " +
+ "the main thread")
+ }
+
+ // Make sure we don't run the launch animation from the same view twice at the same time.
+ if (view.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
+ Log.e(TAG, "Not running dialog launch animation as there is already one running")
+ dialog.show()
+ return dialog
+ }
+
+ view.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
+
+ val launchAnimation = DialogLaunchAnimation(
+ context, launchAnimator, hostDialogProvider, view,
+ onDialogDismissed = { currentAnimations.remove(it) }, originalDialog = dialog)
+ val hostDialog = launchAnimation.hostDialog
+ currentAnimations.add(launchAnimation)
+
+ // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the
+ // host dialog.
+ if (dialog is ListenableDialog) {
+ dialog.addListener(object : DialogListener {
+ override fun onDismiss() {
+ dialog.removeListener(this)
+ hostDialog.dismiss()
+ }
+
+ override fun onHide() {
+ if (launchAnimation.ignoreNextCallToHide) {
+ launchAnimation.ignoreNextCallToHide = false
+ return
+ }
+
+ hostDialog.hide()
+ }
+
+ override fun onShow() {
+ hostDialog.show()
+
+ // We don't actually want to show the original dialog, so hide it.
+ launchAnimation.ignoreNextCallToHide = true
+ dialog.hide()
+ }
+ })
+ }
+
+ launchAnimation.start()
+ return hostDialog
+ }
+
+ /** Notify the current doze amount, to ensure that dialogs fade out when dozing. */
+ // TODO(b/193634619): Replace this by some mandatory constructor parameter to make sure that we
+ // don't forget to call this when the doze amount changes.
+ fun onDozeAmountChanged(amount: Float) {
+ currentAnimations.forEach { it.onDozeAmountChanged(amount) }
+ }
+
+ /**
+ * Ensure that all dialogs currently shown won't animate into their touch surface when
+ * dismissed.
+ *
+ * This is a temporary API meant to be called right before we both dismiss a dialog and start
+ * an activity, which currently does not look good if we animate the dialog into the touch
+ * surface at the same time as the activity starts.
+ *
+ * TODO(b/193634619): Remove this function and animate dialog into opening activity instead.
+ */
+ fun disableAllCurrentDialogsExitAnimations() {
+ currentAnimations.forEach { it.exitAnimationDisabled = true }
+ }
+}
+
+interface HostDialogProvider {
+ /**
+ * Create a host dialog that will be used to host a launch animation. This host dialog must:
+ * 1. call [onCreateCallback] in its onCreate() method, e.g. right after calling
+ * super.onCreate().
+ * 2. call [dismissOverride] instead of doing any dismissing logic. The actual dismissing
+ * logic should instead be done inside the lambda passed to [dismissOverride], which will
+ * be called after the exit animation.
+ *
+ * See SystemUIHostDialogProvider for an example of implementation.
+ */
+ fun createHostDialog(
+ context: Context,
+ theme: Int,
+ onCreateCallback: () -> Unit,
+ dismissOverride: (() -> Unit) -> Unit
+ ): Dialog
+}
+
+/** A dialog to/from which we can add/remove listeners. */
+interface ListenableDialog {
+ /** Add [listener] to the listeners. */
+ fun addListener(listener: DialogListener)
+
+ /** Remove [listener] from the listeners. */
+ fun removeListener(listener: DialogListener)
+}
+
+interface DialogListener {
+ /** Called when this dialog dismiss() is called. */
+ fun onDismiss()
+
+ /** Called when this dialog hide() is called. */
+ fun onHide()
+
+ /** Called when this dialog show() is called. */
+ fun onShow()
+}
+
+private class DialogLaunchAnimation(
+ private val context: Context,
+ private val launchAnimator: LaunchAnimator,
+ hostDialogProvider: HostDialogProvider,
+
+ /** The view that triggered the dialog after being tapped. */
+ private val touchSurface: View,
+
+ /**
+ * A callback that will be called with this [DialogLaunchAnimation] after the dialog was
+ * dismissed and the exit animation is done.
+ */
+ private val onDialogDismissed: (DialogLaunchAnimation) -> Unit,
+
+ /** The original dialog whose content will be shown and animate in/out in [hostDialog]. */
+ private val originalDialog: Dialog
+) {
+ /**
+ * The fullscreen dialog to which we will add the content view [originalDialogView] of
+ * [originalDialog].
+ */
+ val hostDialog = hostDialogProvider.createHostDialog(
+ context, R.style.HostDialogTheme, this::onHostDialogCreated, this::onHostDialogDismissed)
+
+ /** The root content view of [hostDialog]. */
+ private val hostDialogRoot = FrameLayout(context)
+
+ /**
+ * The content view of [originalDialog], which will be stolen from that dialog and added to
+ * [hostDialogRoot].
+ */
+ private var originalDialogView: View? = null
+
+ /**
+ * The background color of [originalDialogView], taking into consideration the [originalDialog]
+ * window background color.
+ */
+ private var originalDialogBackgroundColor = Color.BLACK
+
+ /**
+ * Whether we are currently launching/showing the dialog by animating it from [touchSurface].
+ */
+ private var isLaunching = true
+
+ /** Whether we are currently dismissing/hiding the dialog by animating into [touchSurface]. */
+ private var isDismissing = false
+
+ private var dismissRequested = false
+ private var drawHostDialog = false
+ var ignoreNextCallToHide = false
+ var exitAnimationDisabled = false
+
+ fun start() {
+ // Show the host (fullscreen) dialog, to which we will add the stolen dialog view.
+ hostDialog.show()
+
+ // Steal the dialog view. We do that by showing it but preventing it from drawing, then
+ // hiding it as soon as its content is available.
+ stealOriginalDialogContentView(then = this::showDialogFromView)
+ }
+
+ private fun onHostDialogCreated() {
+ // Make the dialog fullscreen with a transparent background.
+ hostDialog.setContentView(
+ hostDialogRoot,
+ ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ )
+
+ val window = hostDialog.window
+ ?: throw IllegalStateException("There is no window associated to the host dialog")
+ window.setBackgroundDrawableResource(android.R.color.transparent)
+ window.setLayout(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT
+ )
+
+ // Prevent the host dialog from drawing until the animation starts.
+ hostDialogRoot.viewTreeObserver.addOnPreDrawListener(
+ object : ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ if (drawHostDialog) {
+ hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this)
+ return true
+ }
+
+ return false
+ }
+ }
+ )
+ }
+
+ /** Get the content view of [originalDialog] and pass it to [then]. */
+ private fun stealOriginalDialogContentView(then: (View) -> Unit) {
+ // The original dialog content view will be attached to android.R.id.content when the dialog
+ // is shown, so we show the dialog and add an observer to get the view but also prevents the
+ // original dialog from being drawn.
+ val androidContent = originalDialog.findViewById<ViewGroup>(android.R.id.content)
+ ?: throw IllegalStateException("Dialog does not have any android.R.id.content view")
+
+ androidContent.viewTreeObserver.addOnPreDrawListener(
+ object : ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ if (androidContent.childCount == 1) {
+ androidContent.viewTreeObserver.removeOnPreDrawListener(this)
+
+ // Hide the animated dialog. Because of the dialog listener set up
+ // earlier, this would also hide the host dialog, but in this case we
+ // need to keep the host dialog visible.
+ ignoreNextCallToHide = true
+ originalDialog.hide()
+
+ then(androidContent.getChildAt(0))
+ return false
+ }
+
+ // Never draw the original dialog content.
+ return false
+ }
+ })
+ originalDialog.show()
+ }
+
+ private fun showDialogFromView(dialogView: View) {
+ // Save the dialog view for later as we will need it for the close animation.
+ this.originalDialogView = dialogView
+
+ // Close the dialog when clicking outside of it.
+ hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
+ dialogView.isClickable = true
+
+ // Set the background of the window dialog to the dialog itself.
+ // TODO(b/193634619): Support dialog windows without background.
+ // TODO(b/193634619): Support dialog whose background comes from the content view instead of
+ // the window.
+ val typedArray =
+ originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window)
+ val backgroundRes =
+ typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0)
+ typedArray.recycle()
+ if (backgroundRes == 0) {
+ throw IllegalStateException("Dialogs with no backgrounds on window are not supported")
+ }
+
+ dialogView.setBackgroundResource(backgroundRes)
+ originalDialogBackgroundColor =
+ GhostedViewLaunchAnimatorController.findGradientDrawable(dialogView.background!!)
+ ?.color
+ ?.defaultColor ?: Color.BLACK
+
+ // Add the dialog view to the host (fullscreen) dialog and make it invisible to make sure
+ // it's not drawn yet.
+ (dialogView.parent as? ViewGroup)?.removeView(dialogView)
+ hostDialogRoot.addView(
+ dialogView,
+
+ // We give it the size of its original dialog window.
+ FrameLayout.LayoutParams(
+ originalDialog.window.attributes.width,
+ originalDialog.window.attributes.height,
+ Gravity.CENTER
+ )
+ )
+ dialogView.visibility = View.INVISIBLE
+
+ // Start the animation when the dialog is laid out in the center of the host dialog.
+ dialogView.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ view: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ dialogView.removeOnLayoutChangeListener(this)
+ startAnimation(
+ isLaunching = true,
+ onLaunchAnimationStart = { drawHostDialog = true },
+ onLaunchAnimationEnd = {
+ touchSurface.setTag(R.id.launch_animation_running, null)
+
+ // We hide the touch surface when the dialog is showing. We will make this
+ // view visible again when dismissing the dialog.
+ // TODO(b/193634619): Provide an easy way for views to check if they should
+ // be hidden because of a dialog launch so that they don't override this
+ // visibility when updating/refreshing itself.
+ touchSurface.visibility = View.INVISIBLE
+
+ isLaunching = false
+
+ // dismiss was called during the animation, dismiss again now to actually
+ // dismiss.
+ if (dismissRequested) {
+ hostDialog.dismiss()
+ }
+ }
+ )
+ }
+ })
+ }
+
+ private fun onHostDialogDismissed(actualDismiss: () -> Unit) {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ context.mainExecutor.execute { onHostDialogDismissed(actualDismiss) }
+ return
+ }
+
+ // TODO(b/193634619): Support interrupting the launch animation in the middle.
+ if (isLaunching) {
+ dismissRequested = true
+ return
+ }
+
+ if (isDismissing) {
+ return
+ }
+
+ isDismissing = true
+ hideDialogIntoView { instantDismiss: Boolean ->
+ if (instantDismiss) {
+ originalDialog.hide()
+ hostDialog.hide()
+ }
+
+ originalDialog.dismiss()
+ actualDismiss()
+ }
+ }
+
+ /**
+ * Hide the dialog into the touch surface and call [dismissDialogs] when the animation is done
+ * (passing instantDismiss=true) or if it's skipped (passing instantDismiss=false) to actually
+ * dismiss the dialogs.
+ */
+ private fun hideDialogIntoView(dismissDialogs: (Boolean) -> Unit) {
+ if (!shouldAnimateDialogIntoView()) {
+ Log.i(TAG, "Skipping animation of dialog into the touch surface")
+
+ // If the view is invisible it's probably because of us, so we make it visible again.
+ if (touchSurface.visibility == View.INVISIBLE) {
+ touchSurface.visibility = View.VISIBLE
+ }
+
+ dismissDialogs(false /* instantDismiss */)
+ onDialogDismissed(this@DialogLaunchAnimation)
+ return
+ }
+
+ startAnimation(
+ isLaunching = false,
+ onLaunchAnimationStart = {
+ // Remove the dim background as soon as we start the animation.
+ hostDialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ },
+ onLaunchAnimationEnd = {
+ touchSurface.visibility = View.VISIBLE
+ originalDialogView!!.visibility = View.INVISIBLE
+ dismissDialogs(true /* instantDismiss */)
+ onDialogDismissed(this@DialogLaunchAnimation)
+ }
+ )
+ }
+
+ private fun startAnimation(
+ isLaunching: Boolean,
+ onLaunchAnimationStart: () -> Unit = {},
+ onLaunchAnimationEnd: () -> Unit = {}
+ ) {
+ val dialogView = this.originalDialogView!!
+
+ // Create 2 ghost controllers to animate both the dialog and the touch surface in the host
+ // dialog.
+ val startView = if (isLaunching) touchSurface else dialogView
+ val endView = if (isLaunching) dialogView else touchSurface
+ val startViewController = GhostedViewLaunchAnimatorController(startView)
+ val endViewController = GhostedViewLaunchAnimatorController(endView)
+ startViewController.launchContainer = hostDialogRoot
+ endViewController.launchContainer = hostDialogRoot
+
+ val endState = endViewController.createAnimatorState()
+ val controller = object : LaunchAnimator.Controller {
+ override var launchContainer: ViewGroup
+ get() = startViewController.launchContainer
+ set(value) {
+ startViewController.launchContainer = value
+ endViewController.launchContainer = value
+ }
+
+ override fun createAnimatorState(): LaunchAnimator.State {
+ return startViewController.createAnimatorState()
+ }
+
+ override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+ startViewController.onLaunchAnimationStart(isExpandingFullyAbove)
+ endViewController.onLaunchAnimationStart(isExpandingFullyAbove)
+
+ onLaunchAnimationStart()
+ }
+
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ startViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
+ endViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
+
+ onLaunchAnimationEnd()
+ }
+
+ override fun onLaunchAnimationProgress(
+ state: LaunchAnimator.State,
+ progress: Float,
+ linearProgress: Float
+ ) {
+ startViewController.onLaunchAnimationProgress(state, progress, linearProgress)
+
+ // The end view is visible only iff the starting view is not visible.
+ state.visible = !state.visible
+ endViewController.onLaunchAnimationProgress(state, progress, linearProgress)
+
+ // If the dialog content is complex, its dimension might change during the launch
+ // animation. The animation end position might also change during the exit
+ // animation, for instance when locking the phone when the dialog is open. Therefore
+ // we update the end state to the new position/size. Usually the dialog dimension or
+ // position will change in the early frames, so changing the end state shouldn't
+ // really be noticeable.
+ endViewController.fillGhostedViewState(endState)
+ }
+ }
+
+ launchAnimator.startAnimation(controller, endState, originalDialogBackgroundColor)
+ }
+
+ private fun shouldAnimateDialogIntoView(): Boolean {
+ if (exitAnimationDisabled) {
+ return false
+ }
+
+ // The touch surface should be invisible by now, if it's not then something else changed its
+ // visibility and we probably don't want to run the animation.
+ if (touchSurface.visibility != View.INVISIBLE) {
+ return false
+ }
+
+ // If the touch surface is not attached or one of its ancestors is not visible, then we
+ // don't run the animation either.
+ if (!touchSurface.isAttachedToWindow) {
+ return false
+ }
+
+ return (touchSurface.parent as? View)?.isShown ?: true
+ }
+
+ internal fun onDozeAmountChanged(amount: Float) {
+ val alpha = Interpolators.ALPHA_OUT.getInterpolation(1 - amount)
+ val decorView = this.hostDialog.window?.decorView ?: return
+ if (decorView.hasOverlappingRendering() && alpha > 0.0f &&
+ alpha < 1.0f && decorView.layerType != View.LAYER_TYPE_HARDWARE) {
+ decorView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ }
+ decorView.alpha = alpha
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index b4ffb3f..f7e0d58 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -1,7 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.animation
import android.graphics.Canvas
import android.graphics.ColorFilter
+import android.graphics.Insets
import android.graphics.Matrix
import android.graphics.PixelFormat
import android.graphics.Rect
@@ -42,6 +59,7 @@
override var launchContainer = ghostedView.rootView as ViewGroup
private val launchContainerOverlay: ViewGroupOverlay
get() = launchContainer.overlay
+ private val launchContainerLocation = IntArray(2)
/** The ghost view that is drawn and animated instead of the ghosted view. */
private var ghostView: GhostView? = null
@@ -59,8 +77,12 @@
* [backgroundView].
*/
private var backgroundDrawable: WrappedDrawable? = null
+ private val backgroundInsets by lazy { getBackground()?.opticalInsets ?: Insets.NONE }
private var startBackgroundAlpha: Int = 0xFF
+ private val ghostedViewLocation = IntArray(2)
+ private val ghostedViewState = LaunchAnimator.State()
+
/**
* Return the background of the [ghostedView]. This background will be used to draw the
* background of the background view that is expanding up to the final animation position. This
@@ -103,16 +125,24 @@
return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
}
- override fun createAnimatorState(): ActivityLaunchAnimator.State {
- val location = ghostedView.locationOnScreen
- return ActivityLaunchAnimator.State(
- top = location[1],
- bottom = location[1] + ghostedView.height,
- left = location[0],
- right = location[0] + ghostedView.width,
+ override fun createAnimatorState(): LaunchAnimator.State {
+ val state = LaunchAnimator.State(
topCornerRadius = getCurrentTopCornerRadius(),
bottomCornerRadius = getCurrentBottomCornerRadius()
)
+ fillGhostedViewState(state)
+ return state
+ }
+
+ fun fillGhostedViewState(state: LaunchAnimator.State) {
+ // For the animation we are interested in the area that has a non transparent background,
+ // so we have to take the optical insets into account.
+ ghostedView.getLocationOnScreen(ghostedViewLocation)
+ val insets = backgroundInsets
+ state.top = ghostedViewLocation[1] + insets.top
+ state.bottom = ghostedViewLocation[1] + ghostedView.height - insets.bottom
+ state.left = ghostedViewLocation[0] + insets.left
+ state.right = ghostedViewLocation[0] + ghostedView.width - insets.right
}
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
@@ -144,7 +174,7 @@
}
override fun onLaunchAnimationProgress(
- state: ActivityLaunchAnimator.State,
+ state: LaunchAnimator.State,
progress: Float,
linearProgress: Float
) {
@@ -162,19 +192,47 @@
return
}
- val scale = min(state.widthRatio, state.heightRatio)
- ghostViewMatrix.setValues(initialGhostViewMatrixValues)
- ghostViewMatrix.postScale(scale, scale, state.startCenterX, state.startCenterY)
+ // The ghost and backgrounds views were made invisible earlier. That can for instance happen
+ // when animating a dialog into a view.
+ if (ghostView.visibility == View.INVISIBLE) {
+ ghostView.visibility = View.VISIBLE
+ backgroundView.visibility = View.VISIBLE
+ }
+
+ fillGhostedViewState(ghostedViewState)
+ val leftChange = state.left - ghostedViewState.left
+ val rightChange = state.right - ghostedViewState.right
+ val topChange = state.top - ghostedViewState.top
+ val bottomChange = state.bottom - ghostedViewState.bottom
+
+ val widthRatio = state.width.toFloat() / ghostedViewState.width
+ val heightRatio = state.height.toFloat() / ghostedViewState.height
+ val scale = min(widthRatio, heightRatio)
+
+ launchContainer.getLocationOnScreen(launchContainerLocation)
+ GhostView.calculateMatrix(ghostedView, launchContainer, ghostViewMatrix)
+ ghostViewMatrix.postScale(
+ scale, scale,
+ ghostedViewState.centerX - launchContainerLocation[0],
+ ghostedViewState.centerY - launchContainerLocation[1]
+ )
ghostViewMatrix.postTranslate(
- (state.leftChange + state.rightChange) / 2f,
- (state.topChange + state.bottomChange) / 2f
+ (leftChange + rightChange) / 2f,
+ (topChange + bottomChange) / 2f
)
ghostView.animationMatrix = ghostViewMatrix
- backgroundView.top = state.top
- backgroundView.bottom = state.bottom
- backgroundView.left = state.left
- backgroundView.right = state.right
+ // We need to take into account the background insets for the background position.
+ val insets = backgroundInsets
+ val topWithInsets = state.top - insets.top
+ val leftWithInsets = state.left - insets.left
+ val rightWithInsets = state.right + insets.right
+ val bottomWithInsets = state.bottom + insets.bottom
+
+ backgroundView.top = topWithInsets - launchContainerLocation[1]
+ backgroundView.bottom = bottomWithInsets - launchContainerLocation[1]
+ backgroundView.left = leftWithInsets - launchContainerLocation[0]
+ backgroundView.right = rightWithInsets - launchContainerLocation[0]
val backgroundDrawable = backgroundDrawable!!
backgroundDrawable.wrapped?.let {
@@ -207,7 +265,7 @@
* [drawable] is a [LayerDrawable], this will return the first layer that is a
* [GradientDrawable].
*/
- private fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
+ fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
if (drawable is GradientDrawable) {
return drawable
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
index 659b9fe..2765882 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
@@ -16,6 +16,7 @@
package com.android.systemui.animation;
+import android.graphics.Path;
import android.util.MathUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
@@ -29,7 +30,97 @@
* Utility class to receive interpolators from
*/
public class Interpolators {
- public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /*
+ * ============================================================================================
+ * Emphasized interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The default emphasized interpolator. Used for hero / emphasized movement of content.
+ */
+ public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
+
+ /**
+ * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+ * is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 0.8f, 0.15f);
+
+ /**
+ * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+ * is appearing e.g. when coming from off screen
+ */
+ public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
+ 0.05f, 0.7f, 0.1f, 1f);
+
+
+ /*
+ * ============================================================================================
+ * Standard interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The standard interpolator that should be used on every normal animation
+ */
+ public static final Interpolator STANDARD = new PathInterpolator(
+ 0.2f, 0f, 0f, 1f);
+
+ /**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 1f, 1f);
+
+ /**
+ * The standard decelerating interpolator that should be used on every regular movement of
+ * content that is appearing e.g. when coming from off screen.
+ */
+ public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
+ 0f, 0f, 0f, 1f);
+
+ /*
+ * ============================================================================================
+ * Legacy
+ * ============================================================================================
+ */
+
+ /**
+ * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /**
+ * The default legacy accelerating interpolator as defined in Material 1.
+ * Also known as FAST_OUT_LINEAR_IN.
+ */
+ public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
+
+ /**
+ * The default legacy decelerating interpolator as defined in Material 1.
+ * Also known as LINEAR_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
+
+ /**
+ * Linear interpolator. Often used if the interpolator is for different properties who need
+ * different interpolations.
+ */
+ public static final Interpolator LINEAR = new LinearInterpolator();
+
+ /*
+ * ============================================================================================
+ * Custom interpolators
+ * ============================================================================================
+ */
+
+ public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
+ public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
+ public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
/**
* Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
@@ -37,12 +128,9 @@
*/
public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
new PathInterpolator(0.8f, 0f, 0.6f, 1f);
- public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
- public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
- public static final Interpolator LINEAR = new LinearInterpolator();
public static final Interpolator ACCELERATE = new AccelerateInterpolator();
public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
@@ -72,6 +160,12 @@
public static final Interpolator TOUCH_RESPONSE_REVERSE =
new PathInterpolator(0.9f, 0f, 0.7f, 1f);
+ /*
+ * ============================================================================================
+ * Functions / Utilities
+ * ============================================================================================
+ */
+
/**
* Calculate the amount of overshoot using an exponential falloff function with desired
* properties, where the overshoot smoothly transitions at the 1.0f boundary into the
@@ -122,4 +216,14 @@
return (float) (1f - 0.5f * (1f - Math.cos(3.14159f * oneMinusFrac * oneMinusFrac)));
}
}
+
+ // Create the default emphasized interpolator
+ private static PathInterpolator createEmphasizedInterpolator() {
+ Path path = new Path();
+ // Doing the same as fast_out_extra_slow_in
+ path.moveTo(0f, 0f);
+ path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
+ path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
+ return new PathInterpolator(path);
+ }
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
new file mode 100644
index 0000000..3bf6c5e
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffXfermode
+import android.graphics.drawable.GradientDrawable
+import android.util.Log
+import android.util.MathUtils
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.AnimationUtils
+import android.view.animation.PathInterpolator
+import kotlin.math.roundToInt
+
+private const val TAG = "LaunchAnimator"
+
+/** A base class to animate a window launch (activity or dialog) from a view . */
+class LaunchAnimator @JvmOverloads constructor(
+ context: Context,
+ private val isForTesting: Boolean = false
+) {
+ companion object {
+ internal const val DEBUG = false
+ const val ANIMATION_DURATION = 500L
+ private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
+ private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
+ private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT
+
+ private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f)
+ private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
+
+ /**
+ * Given the [linearProgress] of a launch animation, return the linear progress of the
+ * sub-animation starting [delay] ms after the launch animation and that lasts [duration].
+ */
+ @JvmStatic
+ fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
+ return MathUtils.constrain(
+ (linearProgress * ANIMATION_DURATION - delay) / duration,
+ 0.0f,
+ 1.0f
+ )
+ }
+ }
+
+ /** The interpolator used for the width, height, Y position and corner radius. */
+ private val animationInterpolator = AnimationUtils.loadInterpolator(context,
+ R.interpolator.launch_animation_interpolator_y)
+
+ /** The interpolator used for the X position. */
+ private val animationInterpolatorX = AnimationUtils.loadInterpolator(context,
+ R.interpolator.launch_animation_interpolator_x)
+
+ private val launchContainerLocation = IntArray(2)
+ private val cornerRadii = FloatArray(8)
+
+ /**
+ * A controller that takes care of applying the animation to an expanding view.
+ *
+ * Note that all callbacks (onXXX methods) are all called on the main thread.
+ */
+ interface Controller {
+ /**
+ * The container in which the view that started the animation will be animating together
+ * with the opening window.
+ *
+ * This will be used to:
+ * - Get the associated [Context].
+ * - Compute whether we are expanding fully above the launch container.
+ * - Apply surface transactions in sync with RenderThread when animating an activity
+ * launch.
+ *
+ * This container can be changed to force this [Controller] to animate the expanding view
+ * inside a different location, for instance to ensure correct layering during the
+ * animation.
+ */
+ var launchContainer: ViewGroup
+
+ /**
+ * Return the [State] of the view that will be animated. We will animate from this state to
+ * the final window state.
+ *
+ * Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the
+ * animation.
+ */
+ fun createAnimatorState(): State
+
+ /**
+ * The animation started. This is typically used to initialize any additional resource
+ * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
+ * fully above the [launchContainer].
+ */
+ fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
+
+ /** The animation made progress and the expandable view [state] should be updated. */
+ fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
+
+ /**
+ * The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was
+ * called previously. This is typically used to clean up the resources initialized when the
+ * animation was started.
+ */
+ fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
+ }
+
+ /** The state of an expandable view during a [LaunchAnimator] animation. */
+ open class State(
+ /** The position of the view in screen space coordinates. */
+ var top: Int = 0,
+ var bottom: Int = 0,
+ var left: Int = 0,
+ var right: Int = 0,
+
+ var topCornerRadius: Float = 0f,
+ var bottomCornerRadius: Float = 0f
+ ) {
+ private val startTop = top
+
+ val width: Int
+ get() = right - left
+
+ val height: Int
+ get() = bottom - top
+
+ open val topChange: Int
+ get() = top - startTop
+
+ val centerX: Float
+ get() = left + width / 2f
+
+ val centerY: Float
+ get() = top + height / 2f
+
+ /** Whether the expanding view should be visible or hidden. */
+ var visible: Boolean = true
+ }
+
+ interface Animation {
+ /** Cancel the animation. */
+ fun cancel()
+ }
+
+ /**
+ * Start a launch animation controlled by [controller] towards [endState]. An intermediary
+ * layer with [windowBackgroundColor] will fade in then fade out above the expanding view, and
+ * should be the same background color as the opening (or closing) window. If [drawHole] is
+ * true, then this intermediary layer will be drawn with SRC blending mode while it fades out.
+ *
+ * TODO(b/184121838): Remove [drawHole] and instead make the StatusBar draw this hole instead.
+ */
+ fun startAnimation(
+ controller: Controller,
+ endState: State,
+ windowBackgroundColor: Int,
+ drawHole: Boolean = false
+ ): Animation {
+ val state = controller.createAnimatorState()
+
+ // Start state.
+ val startTop = state.top
+ val startBottom = state.bottom
+ val startLeft = state.left
+ val startRight = state.right
+ val startCenterX = (startLeft + startRight) / 2f
+ val startWidth = startRight - startLeft
+ val startTopCornerRadius = state.topCornerRadius
+ val startBottomCornerRadius = state.bottomCornerRadius
+
+ // End state.
+ var endTop = endState.top
+ var endBottom = endState.bottom
+ var endLeft = endState.left
+ var endRight = endState.right
+ var endCenterX = (endLeft + endRight) / 2f
+ var endWidth = endRight - endLeft
+ val endTopCornerRadius = endState.topCornerRadius
+ val endBottomCornerRadius = endState.bottomCornerRadius
+
+ fun maybeUpdateEndState() {
+ if (endTop != endState.top || endBottom != endState.bottom ||
+ endLeft != endState.left || endRight != endState.right) {
+ endTop = endState.top
+ endBottom = endState.bottom
+ endLeft = endState.left
+ endRight = endState.right
+ endCenterX = (endLeft + endRight) / 2f
+ endWidth = endRight - endLeft
+ }
+ }
+
+ val launchContainer = controller.launchContainer
+ val isExpandingFullyAbove = isExpandingFullyAbove(launchContainer, endState)
+
+ // We add an extra layer with the same color as the dialog/app splash screen background
+ // color, which is usually the same color of the app background. We first fade in this layer
+ // to hide the expanding view, then we fade it out with SRC mode to draw a hole in the
+ // launch container and reveal the opening window.
+ val windowBackgroundLayer = GradientDrawable().apply {
+ setColor(windowBackgroundColor)
+ alpha = 0
+ }
+
+ // Update state.
+ val animator = ValueAnimator.ofFloat(0f, 1f)
+ animator.duration = if (isForTesting) 0 else ANIMATION_DURATION
+ animator.interpolator = Interpolators.LINEAR
+
+ val launchContainerOverlay = launchContainer.overlay
+ var cancelled = false
+ animator.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
+ if (DEBUG) {
+ Log.d(TAG, "Animation started")
+ }
+ controller.onLaunchAnimationStart(isExpandingFullyAbove)
+
+ // Add the drawable to the launch container overlay. Overlays always draw
+ // drawables after views, so we know that it will be drawn above any view added
+ // by the controller.
+ launchContainerOverlay.add(windowBackgroundLayer)
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ if (DEBUG) {
+ Log.d(TAG, "Animation ended")
+ }
+ controller.onLaunchAnimationEnd(isExpandingFullyAbove)
+ launchContainerOverlay.remove(windowBackgroundLayer)
+ }
+ })
+
+ animator.addUpdateListener { animation ->
+ if (cancelled) {
+ // TODO(b/184121838): Cancel the animator directly instead of just skipping the
+ // update.
+ return@addUpdateListener
+ }
+
+ maybeUpdateEndState()
+
+ // TODO(b/184121838): Use reverse interpolators to get the same path/arc as the non
+ // reversed animation.
+ val linearProgress = animation.animatedFraction
+ val progress = animationInterpolator.getInterpolation(linearProgress)
+ val xProgress = animationInterpolatorX.getInterpolation(linearProgress)
+
+ val xCenter = MathUtils.lerp(startCenterX, endCenterX, xProgress)
+ val halfWidth = MathUtils.lerp(startWidth, endWidth, progress) / 2f
+
+ state.top = MathUtils.lerp(startTop, endTop, progress).roundToInt()
+ state.bottom = MathUtils.lerp(startBottom, endBottom, progress).roundToInt()
+ state.left = (xCenter - halfWidth).roundToInt()
+ state.right = (xCenter + halfWidth).roundToInt()
+
+ state.topCornerRadius =
+ MathUtils.lerp(startTopCornerRadius, endTopCornerRadius, progress)
+ state.bottomCornerRadius =
+ MathUtils.lerp(startBottomCornerRadius, endBottomCornerRadius, progress)
+
+ // The expanding view can/should be hidden once it is completely covered by the opening
+ // window.
+ state.visible = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1
+
+ applyStateToWindowBackgroundLayer(
+ windowBackgroundLayer,
+ state,
+ linearProgress,
+ launchContainer,
+ drawHole
+ )
+ controller.onLaunchAnimationProgress(state, progress, linearProgress)
+ }
+
+ animator.start()
+ return object : Animation {
+ override fun cancel() {
+ cancelled = true
+ animator.cancel()
+ }
+ }
+ }
+
+ /** Return whether we are expanding fully above the [launchContainer]. */
+ internal fun isExpandingFullyAbove(launchContainer: View, endState: State): Boolean {
+ launchContainer.getLocationOnScreen(launchContainerLocation)
+ return endState.top <= launchContainerLocation[1] &&
+ endState.bottom >= launchContainerLocation[1] + launchContainer.height &&
+ endState.left <= launchContainerLocation[0] &&
+ endState.right >= launchContainerLocation[0] + launchContainer.width
+ }
+
+ private fun applyStateToWindowBackgroundLayer(
+ drawable: GradientDrawable,
+ state: State,
+ linearProgress: Float,
+ launchContainer: View,
+ drawHole: Boolean
+ ) {
+ // Update position.
+ launchContainer.getLocationOnScreen(launchContainerLocation)
+ drawable.setBounds(
+ state.left - launchContainerLocation[0],
+ state.top - launchContainerLocation[1],
+ state.right - launchContainerLocation[0],
+ state.bottom - launchContainerLocation[1]
+ )
+
+ // Update radius.
+ cornerRadii[0] = state.topCornerRadius
+ cornerRadii[1] = state.topCornerRadius
+ cornerRadii[2] = state.topCornerRadius
+ cornerRadii[3] = state.topCornerRadius
+ cornerRadii[4] = state.bottomCornerRadius
+ cornerRadii[5] = state.bottomCornerRadius
+ cornerRadii[6] = state.bottomCornerRadius
+ cornerRadii[7] = state.bottomCornerRadius
+ drawable.cornerRadii = cornerRadii
+
+ // We first fade in the background layer to hide the expanding view, then fade it out
+ // with SRC mode to draw a hole punch in the status bar and reveal the opening window.
+ val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT)
+ if (fadeInProgress < 1) {
+ val alpha = Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation(fadeInProgress)
+ drawable.alpha = (alpha * 0xFF).roundToInt()
+ } else {
+ val fadeOutProgress = getProgress(
+ linearProgress, ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
+ val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress)
+ drawable.alpha = (alpha * 0xFF).roundToInt()
+
+ if (drawHole) {
+ drawable.setXfermode(SRC_MODE)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
index dd35dd9..1eec820 100644
--- a/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
+++ b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="?android:attr/colorBackground"
+ android:fillColor="?android:attr/textColorPrimaryInverse"
android:pathData="M9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59L17.59,17L14,13.41L10.41,17L9,15.59zM21,6H8l-4.5,6L8,18h13V6M21,4c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H8c-0.63,0 -1.22,-0.3 -1.6,-0.8L1,12l5.4,-7.2C6.78,4.3 7.37,4 8,4H21L21,4z"/>
</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
index b844515..2ad5e54 100644
--- a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
+++ b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
@@ -19,7 +19,8 @@
android:viewportHeight="36"
android:viewportWidth="36"
android:width="36sp">
- <path android:fillColor="?android:attr/colorBackground"
+
+ <path android:fillColor="?android:attr/textColorPrimaryInverse"
android:pathData="M17.59,13.41L21.17,17H7v2h14.17l-3.59,3.59L19,24l6,-6l-6,-6L17.59,
13.41zM26,12v12h2V12H26z"/>
</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml b/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml
new file mode 100644
index 0000000..495fbb8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="24dp"/>
+ <gradient
+ android:angle="0"
+ android:startColor="#00000000"
+ android:endColor="#ff000000"
+ android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 624ee9f..6fd83c5 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -17,7 +17,8 @@
*/
-->
-<resources>
+<resources
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- Keyguard PIN pad styles -->
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
@@ -58,11 +59,11 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NumPadKey.Delete">
- <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+ <item name="android:colorControlNormal">?androidprv:attr/colorAccentSecondaryVariant</item>
<item name="android:src">@drawable/ic_backspace_24dp</item>
</style>
<style name="NumPadKey.Enter">
- <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+ <item name="android:colorControlNormal">?androidprv:attr/colorAccentSecondaryVariant</item>
<item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
</style>
<style name="Widget.TextView.NumPadKey.Klondike"
diff --git a/packages/SystemUI/res/color/docked_divider_background.xml b/packages/SystemUI/res/color/docked_divider_background.xml
index 85ede9a..2ab8ecd 100644
--- a/packages/SystemUI/res/color/docked_divider_background.xml
+++ b/packages/SystemUI/res/color/docked_divider_background.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
</selector>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
index 6725a26..e626675 100644
--- a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
@@ -14,19 +14,20 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle">
- <solid
- android:color="?androidprv:attr/colorSurface"
- />
- <corners android:radius="20dp" />
-
- <padding
- android:left="20dp"
- android:right="20dp">
- </padding>
-
-</shape>
\ No newline at end of file
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <inset
+ android:insetBottom="4dp"
+ android:insetTop="4dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="20dp" />
+ <padding
+ android:left="20dp"
+ android:right="20dp" />
+ <solid android:color="?androidprv:attr/colorSurface" />
+ </shape>
+ </inset>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index b841419..c1d7308b 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/internet_connectivity_dialog"
- android:layout_width="@dimen/internet_dialog_list_max_width"
+ android:layout_width="@dimen/large_dialog_width"
android:layout_height="@dimen/internet_dialog_list_max_height"
android:background="@drawable/internet_dialog_rounded_top_corner_background"
android:orientation="vertical">
@@ -38,8 +38,7 @@
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
android:layout_height="32dp"
- android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="google-sans"
+ android:textAppearance="@style/TextAppearance.InternetDialog"
android:textSize="24sp"/>
<TextView
@@ -50,8 +49,7 @@
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
- android:fontFamily="google-sans"
- android:textSize="14sp"/>
+ android:textAppearance="@style/TextAppearance.InternetDialog.Secondary"/>
</LinearLayout>
<LinearLayout
@@ -87,24 +85,49 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
- android:id="@+id/internet_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
+ android:id="@+id/ethernet_layout"
+ style="@style/InternetDialog.Network"
+ android:background="@drawable/settingslib_switch_bar_bg_on"
+ android:visibility="gone">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical|start"
+ android:clickable="false">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:autoMirrored="true"
+ android:src="@drawable/stat_sys_ethernet_fully"
+ android:tint="@color/connected_network_primary_color"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:orientation="vertical"
+ android:clickable="false">
+ <TextView
+ android:text="@string/ethernet_label"
+ style="@style/InternetDialog.NetworkTitle.Active"/>
+ <TextView
+ android:text="@string/to_switch_networks_disconnect_ethernet"
+ style="@style/InternetDialog.NetworkSummary.Active"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
android:id="@+id/mobile_network_layout"
- android:layout_width="match_parent"
- android:layout_height="88dp"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
- android:layout_gravity="center_vertical|start"
- android:orientation="horizontal"
- android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:paddingStart="22dp"
- android:paddingEnd="22dp">
+ style="@style/InternetDialog.Network">
<FrameLayout
android:layout_width="24dp"
@@ -121,7 +144,6 @@
<LinearLayout
android:layout_weight="1"
- android:id="@+id/mobile_network_list"
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
@@ -129,28 +151,10 @@
android:gravity="start|center_vertical">
<TextView
android:id="@+id/mobile_title"
- android:textDirection="locale"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginEnd="7dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="16sp"
- android:fontFamily="google-sans"/>
+ style="@style/InternetDialog.NetworkTitle"/>
<TextView
android:id="@+id/mobile_summary"
- android:textDirection="locale"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginEnd="34dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorTertiary"
- android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ style="@style/InternetDialog.NetworkSummary"/>
</LinearLayout>
<FrameLayout
@@ -159,10 +163,11 @@
android:layout_gravity="end|center_vertical">
<Switch
android:id="@+id/mobile_toggle"
+ android:contentDescription="@string/mobile_data_settings_title"
android:switchMinWidth="@dimen/settingslib_switch_track_width"
android:layout_gravity="center"
android:layout_width="@dimen/settingslib_switch_track_width"
- android:layout_height="@dimen/settingslib_switch_track_height"
+ android:layout_height="match_parent"
android:track="@drawable/settingslib_track_selector"
android:thumb="@drawable/settingslib_thumb_selector"
android:theme="@style/MainSwitch.Settingslib"/>
@@ -172,17 +177,11 @@
<LinearLayout
android:id="@+id/turn_on_wifi_layout"
- android:layout_width="match_parent"
+ style="@style/InternetDialog.Network"
android:layout_height="72dp"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
android:gravity="center"
- android:orientation="horizontal"
- android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:paddingStart="22dp"
- android:paddingEnd="22dp">
+ android:clickable="false"
+ android:focusable="false">
<FrameLayout
android:layout_weight="1"
@@ -193,13 +192,10 @@
<TextView
android:id="@+id/wifi_toggle_title"
android:text="@string/turn_on_wifi"
- android:textDirection="locale"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center_vertical"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="16sp"
- android:fontFamily="google-sans"/>
+ android:textAppearance="@style/TextAppearance.InternetDialog"/>
</FrameLayout>
<FrameLayout
@@ -209,10 +205,11 @@
android:layout_marginBottom="10dp">
<Switch
android:id="@+id/wifi_toggle"
+ android:contentDescription="@string/turn_on_wifi"
android:switchMinWidth="@dimen/settingslib_switch_track_width"
android:layout_gravity="center"
android:layout_width="@dimen/settingslib_switch_track_width"
- android:layout_height="@dimen/settingslib_switch_track_height"
+ android:layout_height="match_parent"
android:track="@drawable/settingslib_track_selector"
android:thumb="@drawable/settingslib_thumb_selector"
android:theme="@style/MainSwitch.Settingslib"/>
@@ -222,18 +219,12 @@
<LinearLayout
android:id="@+id/wifi_connected_layout"
- android:layout_width="match_parent"
+ style="@style/InternetDialog.Network"
android:layout_height="72dp"
- android:layout_gravity="center_vertical|start"
- android:clickable="true"
- android:focusable="true"
- android:visibility="gone"
- android:background="?android:attr/selectableItemBackground"
- android:orientation="horizontal"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
android:paddingStart="20dp"
- android:paddingEnd="24dp">
+ android:paddingEnd="24dp"
+ android:background="@drawable/settingslib_switch_bar_bg_on"
+ android:visibility="gone">
<FrameLayout
android:layout_width="24dp"
@@ -248,7 +239,6 @@
</FrameLayout>
<LinearLayout
- android:id="@+id/wifi_connected_list"
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
@@ -258,26 +248,11 @@
android:gravity="start|center_vertical">
<TextView
android:id="@+id/wifi_connected_title"
- android:textDirection="locale"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ style="@style/InternetDialog.NetworkTitle.Active"
+ android:textSize="14sp"/>
<TextView
android:id="@+id/wifi_connected_summary"
- android:textDirection="locale"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorTertiary"
- android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ style="@style/InternetDialog.NetworkSummary.Active"/>
</LinearLayout>
<FrameLayout
@@ -340,33 +315,33 @@
android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
<TextView
android:text="@string/see_all_networks"
- android:textDirection="locale"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center_vertical"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ android:textAppearance="@style/TextAppearance.InternetDialog"
+ android:textSize="14sp"/>
</FrameLayout>
-
</LinearLayout>
<FrameLayout
- android:layout_width="match_parent"
+ android:id="@+id/done_layout"
+ android:layout_width="67dp"
android:layout_height="48dp"
- android:layout_marginBottom="40dp">
+ android:layout_marginEnd="24dp"
+ android:layout_marginBottom="40dp"
+ android:layout_gravity="end|center_vertical"
+ android:clickable="true"
+ android:focusable="true">
<Button
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:id="@+id/done"
- android:layout_width="67dp"
- android:layout_height="36dp"
- android:layout_marginEnd="24dp"
- android:layout_gravity="end|center_vertical"
- android:background="@drawable/internet_dialog_footer_background"
- android:textColor="?android:attr/textColorPrimary"
android:text="@string/inline_done_button"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="match_parent"
+ android:layout_height="36dp"
+ android:layout_gravity="center"
+ android:textAppearance="@style/TextAppearance.InternetDialog"
android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ android:background="@drawable/internet_dialog_footer_background"
+ android:clickable="false"/>
</FrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
index 05352c5..868331e 100644
--- a/packages/SystemUI/res/layout/internet_list_item.xml
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -24,21 +24,15 @@
<LinearLayout
android:id="@+id/wifi_list"
- android:layout_width="match_parent"
+ style="@style/InternetDialog.Network"
android:layout_height="72dp"
- android:layout_gravity="center_vertical|start"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
- android:orientation="horizontal"
android:paddingStart="20dp"
- android:paddingEnd="40dp">
+ android:paddingEnd="24dp">
<FrameLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:clickable="false"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+ android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/wifi_icon"
android:layout_width="wrap_content"
@@ -52,31 +46,16 @@
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="72dp"
+ android:layout_marginEnd="30dp"
android:layout_weight="1"
- android:gravity="start|center_vertical"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+ android:gravity="start|center_vertical">
<TextView
android:id="@+id/wifi_title"
- android:textDirection="locale"
- android:layout_width="wrap_content"
- android:layout_height="20dp"
- android:gravity="start|center_vertical"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:fontFamily="google-sans"
- android:layout_marginEnd="18dp"/>
+ style="@style/InternetDialog.NetworkTitle"
+ android:textSize="14sp"/>
<TextView
android:id="@+id/wifi_summary"
- android:textDirection="locale"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp"
- android:fontFamily="google-sans"
- android:layout_marginEnd="18dp"/>
+ style="@style/InternetDialog.NetworkSummary"/>
</LinearLayout>
<FrameLayout
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index d996cee..b338894 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -18,7 +18,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/media_output_dialog"
- android:layout_width="match_parent"
+ android:layout_width="@dimen/large_dialog_width"
android:layout_height="wrap_content"
android:orientation="vertical">
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index c7e54d4..c3fc669 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -48,20 +48,41 @@
android:id="@+id/recommendation_card_icon"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
+ android:layout_marginTop="@dimen/qs_media_padding"
android:src="@drawable/ic_headset"
- style="@style/MediaPlayer.AppIcon"/>
+ style="@style/MediaPlayer.AppIcon"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
+ app:layout_constraintHorizontal_bias="0"/>
<TextView
android:id="@+id/recommendation_card_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:maxLines="2"
+ android:maxLines="1"
android:text="@string/controls_media_smartspace_rec_title"
android:fontFamily="google-sans-medium"
android:textDirection="locale"
android:textSize="@dimen/qq_aa_media_rec_header_text_size"
- android:breakStrategy="balanced"
- android:hyphenationFrequency="none"/>
+ android:hyphenationFrequency="none"
+ app:layout_constraintTop_toBottomOf="@id/recommendation_card_icon"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
+ app:layout_constraintHorizontal_bias="0"/>
+
+ <View
+ android:id="@+id/recommendation_gradient_view"
+ android:layout_width="@dimen/qs_aa_media_gradient_bg_width"
+ android:layout_height="0dp"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:background="@drawable/qs_media_recommendation_bg_gradient"
+ app:layout_constraintTop_toTopOf="@id/recommendation_card_text"
+ app:layout_constraintBottom_toBottomOf="@id/recommendation_card_text"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
+ app:layout_constraintHorizontal_bias="1"/>
<FrameLayout
android:id="@+id/media_cover1_container"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 566cd25..b546a9c 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -134,6 +134,7 @@
android:background="@drawable/qs_media_light_source"
android:forceHasOverlappingRendering="false">
<LinearLayout
+ android:id="@+id/media_seamless_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="@dimen/qs_seamless_height"
diff --git a/packages/SystemUI/res/layout/privacy_dialog.xml b/packages/SystemUI/res/layout/privacy_dialog.xml
index 459fb66..ee4530c 100644
--- a/packages/SystemUI/res/layout/privacy_dialog.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog.xml
@@ -24,9 +24,9 @@
android:layout_marginEnd="@dimen/ongoing_appops_dialog_side_margins"
android:layout_marginTop="8dp"
android:orientation="vertical"
- android:paddingBottom="12dp"
- android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:paddingTop="12dp"
+ android:paddingHorizontal="@dimen/ongoing_appops_dialog_side_padding"
android:background="@drawable/qs_dialog_bg"
/>
-<!-- 12dp padding bottom so there's 20dp total under the icon -->
-<!-- 8dp padding top, as there's 4dp margin in each row -->
\ No newline at end of file
+<!-- 8dp padding bottom so there's 16dp total under the icon -->
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/privacy_dialog_item.xml b/packages/SystemUI/res/layout/privacy_dialog_item.xml
index 7c8945e..e1f0793 100644
--- a/packages/SystemUI/res/layout/privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog_item.xml
@@ -24,8 +24,6 @@
android:layout_marginTop="4dp"
android:importantForAccessibility="yes"
android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="@dimen/ongoing_appops_dialog_side_padding"
- android:paddingRight="@dimen/ongoing_appops_dialog_side_padding"
android:focusable="true"
>
<!-- 4dp marginTop makes 20dp minimum between icons -->
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 412276d..bbb8df1c 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -30,8 +30,8 @@
style="@style/TextAppearance.NotificationSectionHeaderButton"
android:id="@+id/manage_text"
android:layout_width="wrap_content"
- android:layout_height="40dp"
- android:layout_marginTop="16dp"
+ android:layout_height="48dp"
+ android:layout_marginTop="12dp"
android:layout_gravity="start"
android:background="@drawable/notif_footer_btn_background"
android:focusable="true"
@@ -43,8 +43,8 @@
style="@style/TextAppearance.NotificationSectionHeaderButton"
android:id="@+id/dismiss_text"
android:layout_width="wrap_content"
- android:layout_height="40dp"
- android:layout_marginTop="16dp"
+ android:layout_height="48dp"
+ android:layout_marginTop="12dp"
android:layout_gravity="end"
android:background="@drawable/notif_footer_btn_background"
android:focusable="true"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index bfb6969..f589498 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ontsluit om netwerke te bekyk"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Soek tans na netwerke …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kon nie aan netwerk koppel nie"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index df4eafc..e94dc68 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"አውታረ መረቦችን ለመመልከት ይክፈቱ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"አውታረ መረቦችን በመፈለግ ላይ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ከአውታረ መረቡ ጋር መገናኘት አልተሳካም"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi ለአሁን በራስ-ሰር አይገናኝም"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"አውታረ መረቦችን ለመቀየር፣ የኢተርኔት ግንኙነት ያቋርጡ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 28c2d7c..14753da 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1199,5 +1199,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"فتح القفل لعرض الشبكات"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"جارٍ البحث عن شبكات…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"تعذّر الاتصال بالشبكة."</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"لن يتم الاتصال بشبكة Wi-Fi تلقائيًا في الوقت الحالي."</string>
<string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"للتبديل بين الشبكات، يجب فصل إيثرنت."</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index a5d599c..a50d8c1 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -37,7 +37,7 @@
<string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"বেটাৰী সঞ্চয়কাৰীৰ বিষয়ে"</string>
<string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"অন কৰক"</string>
<string name="battery_saver_start_action" msgid="4553256017945469937">"বেটাৰি সঞ্চয়কাৰী অন কৰক"</string>
- <string name="status_bar_settings_settings_button" msgid="534331565185171556">"ছেটিংসমূহ"</string>
+ <string name="status_bar_settings_settings_button" msgid="534331565185171556">"ছেটিং"</string>
<string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"ৱাই-ফাই"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string>
<string name="status_bar_settings_mute_label" msgid="914392730086057522">"মিউট"</string>
@@ -233,7 +233,7 @@
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> শতাংশ বেটাৰি।"</string>
<string name="accessibility_battery_level_with_estimate" msgid="4843119982547599452">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰী <xliff:g id="PERCENTAGE">%1$s</xliff:g> শতাংশ, প্ৰায় <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"বেটাৰি চাৰ্জ হৈ আছে, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> শতাংশ।"</string>
- <string name="accessibility_settings_button" msgid="2197034218538913880">"ছিষ্টেমৰ ছেটিংসমূহ৷"</string>
+ <string name="accessibility_settings_button" msgid="2197034218538913880">"ছিষ্টেমৰ ছেটিং৷"</string>
<string name="accessibility_notifications_button" msgid="3960913924189228831">"জাননীসমূহ।"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"সকলো জাননীবোৰ চাওক"</string>
<string name="accessibility_remove_notification" msgid="1641455251495815527">"জাননী মচক৷"</string>
@@ -248,9 +248,9 @@
<skip />
<string name="accessibility_notification_dismissed" msgid="4411652015138892952">"জাননী অগ্ৰাহ্য কৰা হৈছে।"</string>
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"জাননী পেনেল।"</string>
- <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ক্ষিপ্ৰ ছেটিংসমূহ।"</string>
+ <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ক্ষিপ্ৰ ছেটিং।"</string>
<string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"বন্ধ স্ক্ৰীন।"</string>
- <string name="accessibility_desc_settings" msgid="6728577365389151969">"ছেটিংসমূহ"</string>
+ <string name="accessibility_desc_settings" msgid="6728577365389151969">"ছেটিং"</string>
<string name="accessibility_desc_recent_apps" msgid="1748675199348914194">"অৱলোকন।"</string>
<string name="accessibility_desc_work_lock" msgid="4355620395354680575">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ লক স্ক্ৰীন"</string>
<string name="accessibility_desc_close" msgid="8293708213442107755">"বন্ধ কৰক"</string>
@@ -318,8 +318,8 @@
<item quantity="other"> ভিতৰত আৰু <xliff:g id="NUMBER_1">%s</xliff:g>টা জাননী আছে।</item>
</plurals>
<string name="notification_summary_message_format" msgid="5158219088501909966">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string>
- <string name="status_bar_notification_inspect_item_title" msgid="6818779631806163080">"জাননীৰ ছেটিংসমূহ"</string>
- <string name="status_bar_notification_app_settings_title" msgid="5050006438806013903">"<xliff:g id="APP_NAME">%s</xliff:g> ছেটিংসমূহ"</string>
+ <string name="status_bar_notification_inspect_item_title" msgid="6818779631806163080">"জাননীৰ ছেটিং"</string>
+ <string name="status_bar_notification_app_settings_title" msgid="5050006438806013903">"<xliff:g id="APP_NAME">%s</xliff:g> ছেটিং"</string>
<string name="accessibility_rotation_lock_off" msgid="3880436123632448930">"আপোনাৰ ফ\'নৰ স্ক্ৰীন স্বয়ংক্ৰিয়ভাৱে ঘূৰিব।"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"স্ক্ৰীন লেণ্ডস্কে\'প দিশত লক কৰা হ’ল।"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"স্ক্ৰীন প\'ৰ্ট্ৰেইট দিশত লক কৰা হ’ল।"</string>
@@ -361,7 +361,7 @@
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"মিডিয়া ডিভাইচ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"জৰুৰীকালীন কল মাত্ৰ"</string>
- <string name="quick_settings_settings_label" msgid="2214639529565474534">"ছেটিংসমূহ"</string>
+ <string name="quick_settings_settings_label" msgid="2214639529565474534">"ছেটিং"</string>
<string name="quick_settings_time_label" msgid="3352680970557509303">"সময়"</string>
<string name="quick_settings_user_label" msgid="1253515509432672496">"মোক"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যৱহাৰকাৰী"</string>
@@ -555,9 +555,9 @@
<string name="disconnect_vpn" msgid="26286850045344557">"ভিপিএন সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"নীতিসমূহ চাওক"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"নিয়ন্ত্ৰণসমূহ চাওক"</string>
- <string name="monitoring_description_named_management" msgid="505833016545056036">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিংসমূহ, কৰ্পৰে’টৰ এক্সেছ, এপ্সমূহ, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিং, কৰ্পৰে’টৰ এক্সেছ, এপ্, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
<string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>এ হয়তো এই ডিভাইচটোৰ সৈতে জড়িত হৈ থকা ডেটা এক্সেছ কৰিব, এপ্ পৰিচালনা কৰিব আৰু এই ডিভাইচটোৰ ছেটিং সলনি কৰিব পাৰিব।\n\nআপোনাৰ যদি কিবা প্ৰশ্ন আছে, তেন্তে <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>ৰ সৈতে যোগাযোগ কৰক।"</string>
- <string name="monitoring_description_management" msgid="4308879039175729014">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিংসমূহ, কৰ্পৰে’টৰ এক্সেছ, এপ্সমূহ, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিং, কৰ্পৰে’টৰ এক্সেছ, এপ্, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"আপোনাৰ প্ৰতিষ্ঠানে এই ডিভাইচটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰিছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"আপোনাৰ প্ৰতিষ্ঠানে আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰিছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"এই ডিভাইচটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰা হৈছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
@@ -569,12 +569,12 @@
<string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"আপোনাৰ ব্যক্তিগত প্ৰ\'ফাইলটো <xliff:g id="VPN_APP">%1$s</xliff:g>ৰে সংযুক্ত হৈ আছে, যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
<string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"আপোনাৰ ডিভাইচটো <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>ৰ দ্বাৰা পৰিচালিত।"</string>
<string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>এ আপোনাৰ ডিভাইচটো পৰিচালনা কৰিবলৈ <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ব্যৱহাৰ কৰে।"</string>
- <string name="monitoring_description_do_body" msgid="7700878065625769970">"আপোনাৰ প্ৰশাসকে আপোনাৰ ডিভাইচৰ লগত জড়িত ছেটিংসমূহ, কৰ্প\'ৰেইট অনুমতি, এপসমূহ, ডেটা আৰু ডিভাইচৰ অৱস্থান সম্পৰ্কীয় তথ্য পৰ্যবেক্ষণ কৰাৰ লগতে পৰিচালনা কৰিব পাৰিব।"</string>
+ <string name="monitoring_description_do_body" msgid="7700878065625769970">"আপোনাৰ প্ৰশাসকে আপোনাৰ ডিভাইচৰ লগত জড়িত ছেটিং, কৰ্প\'ৰেইট এক্সেছ, এপ্, ডেটা আৰু ডিভাইচৰ অৱস্থান সম্পৰ্কীয় তথ্য পৰ্যবেক্ষণ তথা পৰিচালনা কৰিব পাৰে।"</string>
<string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
<string name="monitoring_description_do_learn_more" msgid="645149183455573790">"অধিক জানক"</string>
<string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"আপুনি <xliff:g id="VPN_APP">%1$s</xliff:g> ৰে সংযুক্ত হৈ আছে, ই ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি আপোনাৰ নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
<string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
- <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"ভিপিএন ছেটিংসমূহ খোলক"</string>
+ <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN ছেটিং খোলক"</string>
<string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
<string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"বিশ্বাসী পৰিচয়-পত্ৰসমূহ খোলক"</string>
<string name="monitoring_description_network_logging" msgid="577305979174002252">"আপোনাৰ প্ৰশাসকে নেটৱৰ্ক লগিং অন কৰিছে, যিয়ে আপোনাৰ ডিভাইচটোত নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ কৰে।\n\nএই সম্পৰ্কে অধিক জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
@@ -596,7 +596,7 @@
<string name="hidden_notifications_setup" msgid="2064795578526982467">"ছেট আপ কৰক"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="volume_zen_end_now" msgid="5901885672973736563">"এতিয়া অফ কৰক"</string>
- <string name="accessibility_volume_settings" msgid="1458961116951564784">"ধ্বনিৰ ছেটিংসমূহ"</string>
+ <string name="accessibility_volume_settings" msgid="1458961116951564784">"ধ্বনিৰ ছেটিং"</string>
<string name="accessibility_volume_expand" msgid="7653070939304433603">"সম্প্ৰসাৰণ কৰক"</string>
<string name="accessibility_volume_collapse" msgid="2746845391013829996">"সংকুচিত কৰক"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"স্বয়ংক্ৰিয় কেপশ্বন মিডিয়া"</string>
@@ -621,7 +621,7 @@
<string name="screen_pinning_start" msgid="7483998671383371313">"এপ্টো পিন কৰা হ’ল"</string>
<string name="screen_pinning_exit" msgid="4553787518387346893">"এপ্টো আনপিন কৰা হ’ল"</string>
<string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> লুকুৱাবনে?"</string>
- <string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"আপুনি ইয়াক পৰৱৰ্তী সময়ত ছেটিংসমূহত অন কৰিলে ই পুনৰ প্ৰকট হ\'ব।"</string>
+ <string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"আপুনি পৰৱৰ্তী সময়ত ছেটিঙত ইয়াক অন কৰিলে ই পুনৰ প্ৰকট হ\'ব।"</string>
<string name="quick_settings_reset_confirmation_button" msgid="3341477479055016776">"লুকুৱাওক"</string>
<string name="stream_voice_call" msgid="7468348170702375660">"কল"</string>
<string name="stream_system" msgid="7663148785370565134">"ছিষ্টেম"</string>
@@ -659,7 +659,7 @@
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="6235377891802910455">"সংযুক্ত বেটাৰিৰ কিমান শতাংশ বাকী আছে দেখুওৱাক"</string>
<string name="show_battery_percentage_summary" msgid="9053024758304102915">"চাৰ্জ হৈ নথকা অৱস্থাত বেটাৰি কিমান শতাংশ বাকী স্থিতি দণ্ডৰ ভিতৰত দেখুৱাওক"</string>
- <string name="quick_settings" msgid="6211774484997470203">"ক্ষিপ্ৰ ছেটিংসমূহ"</string>
+ <string name="quick_settings" msgid="6211774484997470203">"ক্ষিপ্ৰ ছেটিং"</string>
<string name="status_bar" msgid="4357390266055077437">"স্থিতি দণ্ড"</string>
<string name="overview" msgid="3522318590458536816">"অৱলোকন"</string>
<string name="demo_mode" msgid="263484519766901593">"ছিষ্টেমৰ UI প্ৰদৰ্শন ম\'ড"</string>
@@ -684,21 +684,21 @@
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
- <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"ক্ষিপ্ৰ ছেটিংসমূহ, <xliff:g id="TITLE">%s</xliff:g>।"</string>
+ <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"ক্ষিপ্ৰ ছেটিং, <xliff:g id="TITLE">%s</xliff:g>।"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"হটস্পট"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"কিছুমানৰ বাবে আমোদজনক হয় কিন্তু সকলোৰে বাবে নহয়"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
<string name="tuner_persistent_warning" msgid="230466285569307806">"এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
<string name="got_it" msgid="477119182261892069">"বুজি পালোঁ"</string>
- <string name="tuner_toast" msgid="3812684836514766951">"অভিনন্দন! ছেটিংসমূহত System UI Tuner যোগ কৰা হৈছে"</string>
- <string name="remove_from_settings" msgid="633775561782209994">"ছেটিংসমূহৰ পৰা আঁতৰাওক"</string>
- <string name="remove_from_settings_prompt" msgid="551565437265615426">"ছেটিংসমূহৰ পৰা System UI Tuner আঁতৰাই ইয়াৰ সুবিধাসমূহ ব্যৱহাৰ কৰাটো বন্ধ কৰিবনে?"</string>
+ <string name="tuner_toast" msgid="3812684836514766951">"অভিনন্দন! ছেটিঙত System UI Tuner যোগ কৰা হৈছে"</string>
+ <string name="remove_from_settings" msgid="633775561782209994">"ছেটিঙৰ পৰা আঁতৰাওক"</string>
+ <string name="remove_from_settings_prompt" msgid="551565437265615426">"ছেটিঙৰ পৰা System UI Tuner আঁতৰাই ইয়াৰ সুবিধাসমূহ ব্যৱহাৰ কৰাটো বন্ধ কৰিবনে?"</string>
<string name="activity_not_found" msgid="8711661533828200293">"আপোনাৰ ডিভাইচত এপ্লিকেশ্বনটো ইনষ্টল কৰা হোৱা নাই"</string>
<string name="clock_seconds" msgid="8709189470828542071">"ঘড়ীৰ ছেকেণ্ড দেখুৱাওক"</string>
<string name="clock_seconds_desc" msgid="2415312788902144817">"স্থিতি দণ্ডত ঘড়ীৰ ছেকেণ্ড দেখুৱাওক। এই কার্যই বেটাৰিৰ অৱস্থাত প্ৰভাৱ পেলাব পাৰে।"</string>
- <string name="qs_rearrange" msgid="484816665478662911">"ক্ষিপ্ৰ ছেটিংসমূহ পুনৰ সজাওক"</string>
- <string name="show_brightness" msgid="6700267491672470007">"দ্ৰুত ছেটিংসমূহত উজ্জ্বলতা দেখুৱাওক"</string>
+ <string name="qs_rearrange" msgid="484816665478662911">"ক্ষিপ্ৰ ছেটিং পুনৰ সজাওক"</string>
+ <string name="show_brightness" msgid="6700267491672470007">"দ্ৰুত ছেটিঙত উজ্জ্বলতা দেখুৱাওক"</string>
<string name="experimental" msgid="3549865454812314826">"পৰীক্ষামূলক"</string>
<string name="enable_bluetooth_title" msgid="866883307336662596">"ব্লুটুথ অন কৰিবনে?"</string>
<string name="enable_bluetooth_message" msgid="6740938333772779717">"আপোনাৰ টেবলেটত আপোনাৰ কীব\'ৰ্ড সংযোগ কৰিবলৈ আপুনি প্ৰথমে ব্লুটুথ অন কৰিব লাগিব।"</string>
@@ -748,7 +748,7 @@
<string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"বাৰ্তালাপৰ জাননীৰ শীৰ্ষত আৰু প্ৰ’ফাইল চিত্ৰ হিচাপে লক স্ক্ৰীনত দেখুৱায়, এটা বাবল হিচাপে দেখা পোৱা যায়"</string>
<string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"বাৰ্তালাপৰ জাননীৰ শীৰ্ষত আৰু প্ৰ’ফাইল চিত্ৰ হিচাপে লক স্ক্ৰীনত দেখুৱায়, অসুবিধা নিদিব ম’ডত ব্যাঘাত জন্মায়"</string>
<string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"বাৰ্তালাপৰ জাননীৰ শীৰ্ষত আৰু প্ৰ’ফাইল চিত্ৰ হিচাপে লক স্ক্ৰীনত দেখুৱায়, এটা বাবল হিচাপে দেখা পোৱা যায়, অসুবিধা নিদিব ম’ডত ব্যাঘাত জন্মায়"</string>
- <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ছেটিংসমূহ"</string>
+ <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ছেটিং"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"অগ্ৰাধিকাৰ"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বাৰ্তালাপৰ সুবিধাসমূহ সমৰ্থন নকৰে"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"এই জাননীসমূহ সংশোধন কৰিব নোৱাৰি।"</string>
@@ -763,7 +763,7 @@
<string name="appops_camera_overlay" msgid="6466845606058816484">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
<string name="appops_mic_overlay" msgid="4609326508944233061">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন ব্য়ৱহাৰ কৰি আছে।"</string>
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন আৰু কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
- <string name="notification_appops_settings" msgid="5208974858340445174">"ছেটিংসমূহ"</string>
+ <string name="notification_appops_settings" msgid="5208974858340445174">"ছেটিং"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ঠিক আছে"</string>
<string name="feedback_alerted" msgid="5192459808484271208">"ছিষ্টেমটোৱে স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া <b>গুৰুত্ব ডিফ’ল্ট</b>লৈ বৃদ্ধি কৰিছে।"</string>
<string name="feedback_silenced" msgid="9116540317466126457">"ছিষ্টেমটোৱে স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া <b>গুৰুত্ব নীৰৱ</b>লৈ হ্ৰাস কৰিছে।"</string>
@@ -791,7 +791,7 @@
<string name="notification_menu_gear_description" msgid="6429668976593634862">"জাননীৰ নিয়ন্ত্ৰণসমূহ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"জাননীক স্নুজ কৰাৰ বিকল্পসমূহ"</string>
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"মোক মনত পেলাই দিব"</string>
- <string name="notification_menu_settings_action" msgid="7085494017202764285">"ছেটিংসমূহ"</string>
+ <string name="notification_menu_settings_action" msgid="7085494017202764285">"ছেটিং"</string>
<string name="snooze_undo" msgid="2738844148845992103">"আনডু কৰক"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>ৰ বাবে স্নুজ কৰক"</string>
<plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
@@ -856,7 +856,7 @@
<string name="battery" msgid="769686279459897127">"বেটাৰি"</string>
<string name="clock" msgid="8978017607326790204">"ঘড়ী"</string>
<string name="headset" msgid="4485892374984466437">"হেডছেট"</string>
- <string name="accessibility_long_click_tile" msgid="210472753156768705">"ছেটিংসমূহ খোলক"</string>
+ <string name="accessibility_long_click_tile" msgid="210472753156768705">"ছেটিং খোলক"</string>
<string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"হেডফ\'ন সংযোগ হৈ আছে"</string>
<string name="accessibility_status_bar_headset" msgid="2699275863720926104">"হেডছেট সংযোগ হৈ আছে"</string>
<string name="data_saver" msgid="3484013368530820763">"ডেটা সঞ্চয়কাৰী"</string>
@@ -920,19 +920,19 @@
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string>
<string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"টাইল যোগ দিয়া হৈছে"</string>
<string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"টাইল আঁতৰোৱা হৈছে"</string>
- <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিঙৰ সম্পাদক।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
- <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিংসমূহ খোলক।"</string>
- <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ক্ষিপ্ৰ ছেটিংসমূহ খোলক।"</string>
- <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ক্ষিপ্ৰ ছেটিংসমূহ বন্ধ কৰক।"</string>
+ <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিং খোলক।"</string>
+ <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ক্ষিপ্ৰ ছেটিং খোলক।"</string>
+ <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ক্ষিপ্ৰ ছেটিং বন্ধ কৰক।"</string>
<string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"এলার্ম ছেট কৰা হ’ল।"</string>
<string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> হিচাপে ছাইন ইন হ’ল"</string>
<string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ব্যৱহাৰকাৰী বাছনি কৰক"</string>
<string name="data_connection_no_internet" msgid="691058178914184544">"ইণ্টাৰনেট সংযোগ নাই"</string>
<string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"বিৱৰণসমূহ খোলক।"</string>
<string name="accessibility_quick_settings_not_available" msgid="6860875849497473854">"<xliff:g id="REASON">%s</xliff:g>ৰ বাবে উপলব্ধ নহয়"</string>
- <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g>ৰ ছেটিংসমূহ খোলক।"</string>
- <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ছেটিংসমূহৰ ক্ৰম সম্পাদনা কৰক।"</string>
+ <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g>ৰ ছেটিং খোলক।"</string>
+ <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ছেটিঙৰ ক্ৰম সম্পাদনা কৰক।"</string>
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"পাৱাৰ মেনু"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>ৰ পৃষ্ঠা <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্ৰীন"</string>
@@ -990,7 +990,7 @@
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ম’বাইল ডেটা অফ কৰিবনে?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"আপুনি <xliff:g id="CARRIER">%s</xliff:g>ৰ জৰিয়তে ডেটা সংযোগ বা ইণ্টাৰনেট সংযোগ নাপাব। কেৱল ৱাই-ফাইৰ যোগেৰে ইণ্টাৰনেট উপলব্ধ হ\'ব।"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"আপোনাৰ বাহক"</string>
- <string name="touch_filtered_warning" msgid="8119511393338714836">"এটা এপে অনুমতি বিচাৰি কৰা অনুৰোধ এটা ঢাকি ধৰা বাবে ছেটিংসমূহে আপোনাৰ উত্তৰ সত্যাপন কৰিব পৰা নাই।"</string>
+ <string name="touch_filtered_warning" msgid="8119511393338714836">"এটা এপে অনুমতি বিচাৰি কৰা অনুৰোধ এটা ঢাকি ধৰা বাবে ছেটিঙৰ পৰা আপোনাৰ উত্তৰ সত্যাপন কৰিব পৰা নাই।"</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>ক <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাবলৈ অনুমতি দিবনে?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ তথ্য পঢ়িব পাৰে"</string>
<string name="slice_permission_text_2" msgid="6758906940360746983">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ ভিতৰত কাৰ্য কৰিব পাৰে"</string>
@@ -1002,7 +1002,7 @@
<string name="no_auto_saver_action" msgid="7467924389609773835">"নালাগে, ধন্যবাদ"</string>
<string name="auto_saver_enabled_title" msgid="4294726198280286333">"বেটাৰি সঞ্চয়কাৰীৰ সময়সূচী অন কৰা অৱস্থাত আছে"</string>
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"বেটাৰি চ্চাৰ্জৰ স্তৰ <xliff:g id="PERCENTAGE">%d</xliff:g>%%তকৈ কম হোৱাৰ লগে লগে বেটাৰি সঞ্চয়কাৰী স্বয়ংক্ৰিয়ভাৱে অন হ’ব।"</string>
- <string name="open_saver_setting_action" msgid="2111461909782935190">"ছেটিংবোৰ"</string>
+ <string name="open_saver_setting_action" msgid="2111461909782935190">"ছেটিং"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"বুজি পালোঁ"</string>
<string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI হীপ ডাম্প কৰক"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"<xliff:g id="APP">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে।"</string>
@@ -1021,8 +1021,8 @@
<string name="device_services" msgid="1549944177856658705">"ডিভাইচ সেৱা"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"কোনো শিৰোনাম নাই"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"আঁতৰাওক"</string>
- <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰা হ’ল। সলনি কৰিবলৈ ছেটিংসমূহ-লৈ যাওক।"</string>
- <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰিবলৈ ছেটিংসমূহ-লৈ যাওক"</string>
+ <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰা হ’ল। সলনি কৰিবলৈ ছেটিঙলৈ যাওক।"</string>
+ <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰিবলৈ ছেটিঙলৈ যাওক"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ষ্টেণ্ডবাই"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"বিবৰ্ধন ৱিণ্ড’"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"বিবৰ্ধন ৱিণ্ড’ৰ নিয়ন্ত্ৰণসমূহ"</string>
@@ -1065,7 +1065,7 @@
<string name="controls_favorite_removed" msgid="5276978408529217272">"সকলো নিয়ন্ত্ৰণ আঁতৰোৱা হৈছে"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"সালসলনিসমূহ ছেভ নহ’ল"</string>
<string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"অন্য এপ্সমূহ চাওক"</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"নিয়ন্ত্ৰণসমূহ ল’ড কৰিবপৰা নগ’ল। এপ্টোৰ ছেটিংসমূহ সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APP">%s</xliff:g> এপ্টো পৰীক্ষা কৰক।"</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"নিয়ন্ত্ৰণসমূহ ল’ড কৰিবপৰা নগ’ল। এপ্টোৰ ছেটিং সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APP">%s</xliff:g> এপ্টো পৰীক্ষা কৰক।"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"সমিল নিয়ন্ত্ৰণসমূহ উপলব্ধ নহয়"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"অন্য"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহত যোগ দিয়ক"</string>
@@ -1088,7 +1088,7 @@
<string name="controls_media_active_session" msgid="3146882316024153337">"বৰ্তমানৰ মিডিয়াৰ ছেশ্বনটো লুকুৱাব নোৱাৰি।"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"অগ্ৰাহ্য কৰক"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
- <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিং"</string>
<string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ত <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ৰ <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ হৈ আছে"</string>
<string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"প্লে’ কৰক"</string>
<string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> খোলক"</string>
@@ -1098,7 +1098,7 @@
<string name="controls_error_retryable" msgid="864025882878378470">"আসোঁৱাহ, পুনৰ চেষ্টা কৰি আছে…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"বিচাৰি পোৱা নগ’ল"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"নিয়ন্ত্ৰণটো উপলব্ধ নহয়"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> এক্সেছ কৰিব পৰা নগ’ল। নিয়ন্ত্ৰণটো এতিয়াও উপলব্ধ আৰু এপ্টোৰ ছেটিংসমূহ সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APPLICATION">%2$s</xliff:g> এপ্টো পৰীক্ষা কৰক।"</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> এক্সেছ কৰিব পৰা নগ’ল। নিয়ন্ত্ৰণটো এতিয়াও উপলব্ধ আৰু এপ্টোৰ ছেটিং সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APPLICATION">%2$s</xliff:g> এপ্টো পৰীক্ষা কৰক।"</string>
<string name="controls_open_app" msgid="483650971094300141">"এপ্টো খোলক"</string>
<string name="controls_error_generic" msgid="352500456918362905">"স্থিতি ল’ড কৰিব নোৱাৰি"</string>
<string name="controls_error_failed" msgid="960228639198558525">"আসোঁৱাহ হৈছে, আকৌ চেষ্টা কৰক"</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটৱর্ক চাবলৈ আনলক কৰক"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটৱৰ্ক সন্ধান কৰি থকা হৈছে…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটৱৰ্কৰ সৈতে সংযোগ কৰিব পৰা নগ\'ল"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এতিয়া ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
<string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটৱৰ্ক সলনি কৰিবলৈ ইথাৰনেটৰ পৰা সংযোগ বিচ্ছিন্ন কৰক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 7a66f4e..e0ef1ec 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Şəbəkələrə baxmaq üçün kilidi açın"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Şəbəkə axtarılır…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Şəbəkəyə qoşulmaq alınmadı"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hələlik avtomatik qoşulmayacaq"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Şəbəkəni dəyişmək üçün etherneti ayırın"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index d7f37e4..31f8014 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1181,5 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte da biste videli mreže"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traže se mreže…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje sa mrežom nije uspelo"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi trenutno ne može da se automatski poveže"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste promenili mrežu, prekinite eternet vezu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index a5d613c..b296aa6 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -703,8 +703,8 @@
<string name="activity_not_found" msgid="8711661533828200293">"Праграма не ўсталявана на вашым тэлефоне"</string>
<string name="clock_seconds" msgid="8709189470828542071">"Паказваць секунды гадзінніка"</string>
<string name="clock_seconds_desc" msgid="2415312788902144817">"Паказваць секунды гадзінніка на панэлі стану. Можа паўплываць на рэсурс акумулятара."</string>
- <string name="qs_rearrange" msgid="484816665478662911">"Змяніць парадак Хуткіх налад"</string>
- <string name="show_brightness" msgid="6700267491672470007">"Паказваць яркасць у Хуткіх наладах"</string>
+ <string name="qs_rearrange" msgid="484816665478662911">"Змяніць парадак хуткіх налад"</string>
+ <string name="show_brightness" msgid="6700267491672470007">"Паказваць яркасць у хуткіх наладах"</string>
<string name="experimental" msgid="3549865454812314826">"Эксперыментальныя"</string>
<string name="enable_bluetooth_title" msgid="866883307336662596">"Уключыць Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="6740938333772779717">"Для падлучэння клавіятуры да планшэта трэба спачатку ўключыць Bluetooth."</string>
@@ -1072,7 +1072,7 @@
<string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"выдаліць з абранага"</string>
<string name="accessibility_control_move" msgid="8980344493796647792">"Перамясціць у пазіцыю <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="controls_favorite_default_title" msgid="967742178688938137">"Сродкі кіравання"</string>
- <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Выберыце элементы кіравання, да якіх вы хочаце мець доступ з Хуткіх налад"</string>
+ <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Выберыце элементы кіравання, да якіх вы хочаце мець доступ з хуткіх налад"</string>
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Каб змяніць парадак элементаў кіравання, утрымлівайце і перацягвайце іх"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Усе элементы кіравання выдалены"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Змяненні не захаваны"</string>
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблакіраваць для прагляду сетак"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Выконваецца пошук сетак…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не ўдалося падключыцца да сеткі"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Аўтаматычнае падключэнне да Wi-Fi адсутнічае"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Каб падключыцца да сетак, выключыце Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index eee5adb..84c2291 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Отключване с цел преглед на мрежите"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Търсят се мрежи…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Свързването с мрежата не бе успешно"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Засега Wi-Fi няма да се свързва автоматично"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За да превключите мрежите, прекъснете връзката с Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 59cb64c..93ac040 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1175,5 +1175,8 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটওয়ার্ক দেখার জন্য আনলক করুন"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string>
+ <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
+ <skip />
<string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটওয়ার্ক বদলাতে ইথারনেট ডিসকানেক্ট করুন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index af33862..c8588ec 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1181,5 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte da vidite mreže"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index f99ca66..cfe70c2 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueja per veure xarxes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"S\'estan cercant xarxes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"No s\'ha pogut connectar a la xarxa"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Per ara la Wi‑Fi no es connectarà automàticament"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per canviar de xarxa, desconnecta la connexió Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index a128bf1..3d648cb 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Sítě uvidíte po odemknutí"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhledávání sítí…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Připojení k síti se nezdařilo"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se prozatím nebude připojovat automaticky"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pokud chcete přepnout sítě, odpojte ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index a948a03..eb715aa 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås op for at se netværk"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søger efter netværk…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Der kunne ikke oprettes forbindelse til netværket"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Ingen automatisk forbindelse til Wi-Fi i øjeblikket"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Afbryd ethernetforbindelsen for at skifte netværk"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 4d00de5..b815492 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Entsperren, um Netzwerke anzuzeigen"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netzwerke werden gesucht…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Netzwerkverbindung konnte nicht hergestellt werden"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Zurzeit wird keine automatische WLAN-Verbindung hergestellt"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Trenne das Ethernetkabel, um das Netzwerk zu wechseln"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0d5c959..c6cc7f0 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ξεκλειδώστε για προβολή δικτύων"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Αναζήτηση δικτύων…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Αποτυχία σύνδεσης στο δίκτυο"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Δεν θα γίνεται προς το παρόν αυτόματη σύνδεση Wi-Fi."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Για εναλλαγή δικτύων, αποσυνδέστε το ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index e47680e..e1006fc 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 43f30bb..66b44d5 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e47680e..e1006fc 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index e47680e..e1006fc 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index e0ff8b5..9af7322 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 8533c12..8d53bb9 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea para ver las redes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Se produjo un error al establecer conexión con la red"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora, el Wi-Fi no se conectará automáticamente"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconéctate de Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 533f368..c0c12fa 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -683,7 +683,7 @@
<string name="zen_alarm_warning_indef" msgid="5252866591716504287">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>) a menos que desactives esta opción antes"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
<string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string>
- <string name="alarm_template_far" msgid="3561752195856839456">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_quick_settings_detail" msgid="544463655956179791">"Ajustes rápidos, <xliff:g id="TITLE">%s</xliff:g>."</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Compartir Internet"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea para ver redes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"No se ha podido conectar a la red"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora no se conectará automáticamente a redes Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconecta el cable Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 5bc39f13..c3cb552 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Võrkude vaatamiseks avage"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Võrkude otsimine …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Võrguühenduse loomine ebaõnnestus"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi-ühendust ei looda praegu automaatselt"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Võrkude vahetamiseks katkestage Etherneti-ühendus"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index aaa1387..8df329a 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -845,7 +845,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Arakatzailea"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Kontaktuak"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"Posta"</string>
- <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS mezuak"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMSak"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Musika"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="5078136084632450333">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Sareak ikusteko, desblokeatu pantaila"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Sareak bilatzen…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ezin izan da konektatu sarera"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Oraingoz ez da automatikoki konektatuko wifira"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Sarea aldatzeko, deskonektatu Ethernet-a"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index a97fffb..ae308b7 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"برای مشاهده شبکهها، قفل صفحه را باز کنید"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"درحال جستجوی شبکه…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"اتصال به شبکه برقرار نشد"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"فعلاً Wi-Fi بهطور خودکار متصل نمیشود"</string>
<string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"برای تغییر شبکه، اترنت را قطع کنید"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 662a6d6..ee0cdb6 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Avaa lukitus nähdäksesi verkot"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Etsitään verkkoja…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yhteyden muodostaminen verkkoon epäonnistui"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ei toistaiseksi yhdistä automaattisesti"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Irrota Ethernet-johto, jos haluat vaihtaa verkkoa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 249f126..7fcbcfb 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Déverrouillez l\'écran pour afficher les réseaux Wi-Fi"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux en cours…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi impossible pour le moment"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, débranchez le câble Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 77ed144..74e55fc 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Déverrouiller pour afficher les réseaux"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi désactivée pour le moment"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, déconnectez l\'Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 95f4731..3bcfcc8 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea a pantalla para ver as redes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Produciuse un erro ao conectarse á rede"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"De momento, a wifi non se conectará automaticamente"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de rede, desconecta a Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 829b056..7928115 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1175,5 +1175,8 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"વાઇ-ફાઇ નેટવર્ક જોવા માટે અનલૉક કરો"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string>
+ <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
+ <skip />
<string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"બીજા નેટવર્ક પર જવા માટે, ઇથરનેટ ડિસ્કનેક્ટ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index dd4617d..edd7fc6 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"वाई-फ़ाई नेटवर्क देखने के लिए, स्क्रीन को अनलॉक करें"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क खोजे जा रहे हैं…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्क से कनेक्ट नहीं किया जा सका"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"फ़िलहाल, वाई-फ़ाई अपने-आप कनेक्ट नहीं होगा"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदलने के लिए, पहले ईथरनेट को डिसकनेक्ट करें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index cf465a1..b066191 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1181,5 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte za prikaz mreža"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se zasad neće automatski povezivati"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste se prebacili na drugu mrežu, odspojite Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 20a973b..5880cf0 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Zárolás feloldása a hálózatok megtekintéséhez"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Hálózatok keresése…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nem sikerült hálózathoz csatlakozni."</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A Wi-Fi-re történő csatlakozás jelenleg nem automatikus"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Hálózatváltáshoz válassza le az ethernetet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 17d7d75..ad3cf25 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ապակողպեք՝ ցանցերը դիտելու համար"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ցանցերի որոնում…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Չհաջողվեց միանալ ցանցին"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-ն ավտոմատ չի միանա"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Մի ցանցից մյուսին անցնելու համար անջատեք Ethernet-ը"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a4743c1..c42b143 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Buka kunci untuk melihat jaringan"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari jaringan …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menghubungkan ke jaringan"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan otomatis terhubung untuk saat ini"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk beralih jaringan, lepaskan kabel ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index d2dbf8d..4692280 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Taktu úr lás til að skoða netkerfi"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Leitar að netum…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ekki tókst að tengjast neti"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tengist ekki sjálfkrafa eins og er"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aftengdu ethernet til að skipta um net"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 81af801..1d96598 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Sblocca per visualizzare le reti"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ricerca di reti in corso…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Connessione alla rete non riuscita"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connessione automatica rete Wi-Fi non attiva al momento"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per cambiare rete, scollega il cavo Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 0fa9c9a..8fd2bb9 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"צריך לבטל את הנעילה כדי להציג את הרשתות"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"בתהליך חיפוש רשתות…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"נכשל הניסיון להתחבר לרשת"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string>
<string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 25f7234..3134f73 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ネットワークを表示するにはロック解除してください"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ネットワークを検索しています…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ネットワークに接続できませんでした"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi に自動接続しません"</string>
<string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ネットワークを変更するにはイーサネット接続を解除してください"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 2a73634..c4c73c5 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"განბლოკვა ქსელების სანახავად"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"მიმდინარეობს ქსელების ძიება…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ქსელთან დაკავშირება ვერ ხერხდება"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ინტერნეტს დროებით ავტომატურად არ დაუკავშირდება"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ქსელების გადასართავად, გაწყვიტეთ Ethernet-თან კავშირი"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b09811a..bccc0ab 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Желілерді көру үшін құлыпты ашыңыз."</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Маңайдағы желілер ізделуде…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Желіге қосылмады."</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Әзірше Wi-Fi автоматты түрде қосылмайды."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Желілерді ауыстыру үшін ethernet кабелін ажыратыңыз."</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index ff8aadd..7d79023 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ដោះសោដើម្បីមើលបណ្ដាញ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"កំពុងស្វែងរកបណ្ដាញ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"មិនអាចភ្ជាប់បណ្ដាញបានទេ"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិក្នុងពេលនេះទេ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 07fc111..0a28735 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1175,5 +1175,8 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
+ <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
+ <skip />
<string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಬದಲಿಸಲು, ಇಥರ್ನೆಟ್ ಅನ್ನು ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 7e729d1..3a8657b 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"네트워크를 보려면 잠금 해제하세요"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"네트워크 검색 중…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"네트워크에 연결하지 못했습니다."</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"지금은 Wi-Fi가 자동으로 연결되지 않습니다."</string>
<string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"네트워크를 전환하려면 이더넷을 연결 해제하세요."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d9f1d2c..9f242f34 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Тармактарды көрүү үчүн кулпусун ачыңыз"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Тармактар изделүүдө…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Тармакка туташпай калды"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi азырынча автоматтык түрдө туташпайт"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Башка тармактарга которулуу үчүн Ethernet кабелин ажыратыңыз"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index c4fa48b..afd914d 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ປົດລັອກເພື່ອເບິ່ງເຄືອຂ່າຍ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ກຳລັງຊອກຫາເຄືອຂ່າຍ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ເຊື່ອມຕໍ່ເຄືອຂ່າຍບໍ່ສຳເລັດ"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດສຳລັບຕອນນີ້"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ເພື່ອສະຫຼັບເຄືອຂ່າຍ, ໃຫ້ຕັດການເຊື່ອມຕໍ່ອີເທີເນັດກ່ອນ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 3a35355..233610f 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Atrakinkite, kad peržiūrėtumėte visus tinklus"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ieškoma tinklų…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Jungiantis prie tinklo įvyko klaida"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"„Wi-Fi“ šiuo metu nebus prijungtas automatiškai"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Norėdami perjungti tinklus, atjunkite eternetą"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index e5eaac5..83e6d6e 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1181,5 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Lai skatītu tīklus, atbloķējiet"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Notiek tīklu meklēšana…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Neizdevās izveidot savienojumu ar tīklu"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi savienojums īslaicīgi netiks izveidots automātiski."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Lai pārslēgtu tīklus, atvienojiet tīkla Ethernet vadu."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 85771e3..e00108a 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Отклучете за да се прикажат мрежите"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Се пребаруваат мрежи…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не успеа да се поврзе на мрежата"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi нема да се поврзува автоматски засега"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За промена на мрежата, прекинете ја врската со етернетот"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index db14ca8..49beea2 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"നെറ്റ്വർക്കുകൾ കാണാൻ അൺലോക്ക് ചെയ്യുക"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"നെറ്റ്വർക്കുകൾ തിരയുന്നു…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"നെറ്റ്വർക്കിൽ കണക്റ്റ് ചെയ്യാനായില്ല"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"വൈഫൈ ഇപ്പോൾ സ്വയമേവ കണക്റ്റ് ചെയ്യില്ല"</string>
<string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"മറ്റ് നെറ്റ്വർക്കുകളിലേക്ക് മാറാൻ, ഇതർനെറ്റ് വിച്ഛേദിക്കുക"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 15414c3..f6dea18 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Сүлжээг харахын тулд түгжээг тайлах"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Сүлжээ хайж байна…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Сүлжээнд холбогдож чадсангүй"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-г одоогоор автоматаар холбохгүй"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Сүлжээг сэлгэхийн тулд этернэтийг салгана уу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 1a4b3a1..6156cf5 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -688,7 +688,7 @@
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"हॉटस्पॉट"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाईल"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"सर्वांसाठी नाही तर काहींसाठी मजेदार असू शकते"</string>
- <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनर आपल्याला Android वापरकर्ता इंटरफेस ट्विक आणि कस्टमाइझ करण्याचे अनेक प्रकार देते. ही प्रयोगात्मक वैशिष्ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्यातील रिलीज मध्ये कदाचित दिसणार नाहीत. सावधगिरी बाळगून पुढे सुरू ठेवा."</string>
+ <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनर आपल्याला Android यूझर इंटरफेस ट्विक आणि कस्टमाइझ करण्याचे अनेक प्रकार देते. ही प्रयोगात्मक वैशिष्ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्यातील रिलीज मध्ये कदाचित दिसणार नाहीत. सावधगिरी बाळगून पुढे सुरू ठेवा."</string>
<string name="tuner_persistent_warning" msgid="230466285569307806">"ही प्रयोगात्मक वैशिष्ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्यातील रिलीज मध्ये कदाचित दिसणार नाहीत."</string>
<string name="got_it" msgid="477119182261892069">"समजले"</string>
<string name="tuner_toast" msgid="3812684836514766951">"अभिनंदन! सिस्टम UI ट्युनर सेटिंग्जमध्ये जोडले गेले आहे"</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"नेटवर्क पाहण्यासाठी स्क्रीन अनलॉक करा"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क शोधत आहे…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कशी कनेक्ट करता आले नाही"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"सध्या वाय-फाय ऑटो-कनेक्ट होणार नाही"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क स्विच करण्यासाठी, इथरनेट केबल डिस्कनेक्ट करा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 554b087..9dde6b6 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Buka kunci untuk melihat rangkaian"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari rangkaian…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menyambung kepada rangkaian"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan disambungkan secara automatik buat masa ini"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk menukar rangkaian, putuskan sambungan ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index c19dd2f..50fb26c 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ကွန်ရက်များကြည့်ရန် ဖွင့်ပါ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ကွန်ရက်များကို ရှာဖွေနေသည်…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ကွန်ရက်သို့ ချိတ်ဆက်၍မရပါ"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi က ယခု အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ကွန်ရက်ပြောင်းရန် အီသာနက်ကို ချိတ်ဆက်မှုဖြုတ်ပါ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5deb612..d4eef72 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås opp for å se nettverk"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søker etter nettverk …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kunne ikke koble til nettverket"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi kobles ikke til automatisk inntil videre"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"For å bytte nettverk, koble fra Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index d4362d5..ce1787c 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"नेटवर्कहरू हेर्न आफ्नो स्क्रिन अनलक गर्नुहोस्"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्कहरू खोजिँदै छन्…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कमा कनेक्ट गर्न सकिएन"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"केही समयका लागि Wi-Fi स्वतः कनेक्ट हुँदैन"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदल्न इथरनेट डिस्कनेक्ट गर्नुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 461505f..ffcc3a8 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -48,4 +48,16 @@
<item name="android:windowLightStatusBar">false</item>
</style>
+ <style name="TextAppearance.InternetDialog.Active">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/connected_network_primary_color</item>
+ <item name="android:textDirection">locale</item>
+ </style>
+
+ <style name="TextAppearance.InternetDialog.Secondary.Active">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@color/connected_network_secondary_color</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 8c9a878..ad1403e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ontgrendel het scherm om netwerken te bekijken"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netwerken zoeken…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kan geen verbinding maken met het netwerk"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi maakt momenteel niet automatisch verbinding"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Verbreek de ethernetverbinding om van netwerk te wisselen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index b43e902..e5c2a2b 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ନେଟୱାର୍କଗୁଡ଼ିକୁ ଦେଖିବା ପାଇଁ ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ନେଟୱାର୍କଗୁଡ଼ିକ ସନ୍ଧାନ କରାଯାଉଛି…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ନେଟୱାର୍କକୁ ସଂଯୋଗ କରିବାରେ ବିଫଳ ହୋଇଛି"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ବର୍ତ୍ତମାନ ପାଇଁ ୱାଇ-ଫାଇ ସ୍ୱତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ନେଟୱାର୍କ ସ୍ୱିଚ୍ କରିବାକୁ, ଇଥରନେଟ୍ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index d667813..48750cf 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ਨੈੱਟਵਰਕ ਖੋਜੇ ਜਾ ਰਹੇ ਹਨ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ਫ਼ਿਲਹਾਲ ਵਾਈ-ਫਾਈ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਈਥਰਨੈੱਟ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 51fc45e..3635876 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Odblokuj, by wyświetlić sieci"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Szukam sieci…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nie udało się połączyć z siecią"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nie będzie na razie włączać się automatycznie"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aby przełączać sieci, odłącz Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8109c31..8f499f7 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -592,7 +592,7 @@
<string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
<string name="hidden_notifications_title" msgid="1782412844777612795">"Receba notificações mais rápido"</string>
<string name="hidden_notifications_text" msgid="5899627470450792578">"Veja-as antes de desbloquear"</string>
- <string name="hidden_notifications_cancel" msgid="4805370226181001278">"Não, obrigado"</string>
+ <string name="hidden_notifications_cancel" msgid="4805370226181001278">"Agora não"</string>
<string name="hidden_notifications_setup" msgid="2064795578526982467">"Configurar"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="volume_zen_end_now" msgid="5901885672973736563">"Desativar agora"</string>
@@ -617,7 +617,7 @@
<string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Para liberar o app, toque nos botões \"Voltar\" e home e os mantenha pressionados"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Para liberar o app, deslize para cima e mantenha a tela pressionada"</string>
<string name="screen_pinning_positive" msgid="3285785989665266984">"Entendi"</string>
- <string name="screen_pinning_negative" msgid="6882816864569211666">"Não, obrigado"</string>
+ <string name="screen_pinning_negative" msgid="6882816864569211666">"Agora não"</string>
<string name="screen_pinning_start" msgid="7483998671383371313">"App fixado"</string>
<string name="screen_pinning_exit" msgid="4553787518387346893">"App liberado"</string>
<string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A conexão automática ao Wi-Fi ficará indisponível"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 222db52a..b0c65d8 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"A procurar redes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Não foi possível estabelecer ligação à rede"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por agora, o Wi-Fi não irá estabelecer lig. automaticamente"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desligue a Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8109c31..8f499f7 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -592,7 +592,7 @@
<string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
<string name="hidden_notifications_title" msgid="1782412844777612795">"Receba notificações mais rápido"</string>
<string name="hidden_notifications_text" msgid="5899627470450792578">"Veja-as antes de desbloquear"</string>
- <string name="hidden_notifications_cancel" msgid="4805370226181001278">"Não, obrigado"</string>
+ <string name="hidden_notifications_cancel" msgid="4805370226181001278">"Agora não"</string>
<string name="hidden_notifications_setup" msgid="2064795578526982467">"Configurar"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="volume_zen_end_now" msgid="5901885672973736563">"Desativar agora"</string>
@@ -617,7 +617,7 @@
<string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Para liberar o app, toque nos botões \"Voltar\" e home e os mantenha pressionados"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Para liberar o app, deslize para cima e mantenha a tela pressionada"</string>
<string name="screen_pinning_positive" msgid="3285785989665266984">"Entendi"</string>
- <string name="screen_pinning_negative" msgid="6882816864569211666">"Não, obrigado"</string>
+ <string name="screen_pinning_negative" msgid="6882816864569211666">"Agora não"</string>
<string name="screen_pinning_start" msgid="7483998671383371313">"App fixado"</string>
<string name="screen_pinning_exit" msgid="4553787518387346893">"App liberado"</string>
<string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A conexão automática ao Wi-Fi ficará indisponível"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 95d2647..c143d53 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1181,5 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Deblocați pentru a vedea rețelele"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Se caută rețele…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nu s-a realizat conexiunea la rețea"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Deocamdată, Wi-Fi nu se poate conecta automat"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 58052ac..752110e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблокируйте, чтобы посмотреть сети Wi-Fi."</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Поиск сетей…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не удалось подключиться к сети"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Подключение по Wi-Fi не установится автоматически."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Чтобы переключиться между сетями, отключите кабель Ethernet."</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index b52ae93..b3a7a8e3 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ජාල බැලීමට අගුලු හරින්න"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ජාල සඳහා සොයමින්…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ජාලය වෙත සම්බන්ධ වීම අසාර්ථක විය"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi දැනට ස්වයං-සබැඳි නොවනු ඇත"</string>
<string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ජාල මාරු කිරීමට, ඊතර්නෙට් විසන්ධි කරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0590d31..f45226b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Odomknutím si zobrazte siete"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhľadávajú sa siete…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nepodarilo sa pripojiť k sieti"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi sa teraz automaticky nepripojí"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ak chcete prepnúť siete, odpojte ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 0d1e1fb..ed75390 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Odklenite za ogled omrežij"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iskanje omrežij …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Vzpostavljanje povezave z omrežjem ni uspelo."</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Vmesnik Wi-Fi trenutno ne bo samodejno vzpostavil povezave."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Če želite preklopiti omrežje, prekinite ethernetno povezavo."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 8a40a3e..dc7e825 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Shkyçe për të parë rrjetet"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Po kërkon për rrjete…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Lidhja me rrjetin dështoi"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nuk do të lidhet automatikisht për momentin"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Për të ndërruar rrjetet, shkëput Ethernet-in"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index fb9d3d4..b486589 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1181,5 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Откључајте да бисте видели мреже"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Траже се мреже…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Повезивање са мрежом није успело"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi тренутно не може да се аутоматски повеже"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Да бисте променили мрежу, прекините етернет везу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index d8e31d2..5db089b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås upp för att visa nätverk"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Du ansluts inte till wifi automatiskt för närvarande"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 27a25c0..60104f6 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -82,7 +82,7 @@
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Imehifadhi picha ya skrini"</string>
- <string name="screenshot_saved_text" msgid="7778833104901642442">"Gusa ili utazame picha ya skrini uliyohifadhi"</string>
+ <string name="screenshot_saved_text" msgid="7778833104901642442">"Gusa ili uone picha ya skrini uliyohifadhi"</string>
<string name="screenshot_failed_title" msgid="3259148215671936891">"Imeshindwa kuhifadhi picha ya skrini"</string>
<string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Ni sharti ufungue kifaa kabla ya kuhifadhi picha ya skrini"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Jaribu kupiga picha ya skrini tena"</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Fungua ili uangalie mitandao"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Inatafuta mitandao…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Imeshindwa kuunganisha kwenye mtandao"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi haitaunganishwa kiotomatiki kwa sasa"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ili kubadili mitandao, tenganisha ethaneti"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
new file mode 100644
index 0000000..02fd25b
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+
+ <!-- The maximum number of tiles in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_tiles">6</integer>
+
+ <!-- The maximum number of rows in the QuickSettings -->
+ <integer name="quick_settings_max_rows">3</integer>
+
+ <!-- The number of columns in the QuickSettings -->
+ <integer name="quick_settings_num_columns">3</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index 40838f3..da2403a 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -16,6 +16,6 @@
-->
<resources>
<!-- Size of the panel of large phones on portrait. This shouldn't fill, but have some padding on the side -->
- <dimen name="notification_panel_width">416dp</dimen>
+ <dimen name="notification_panel_width">504dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index d921d49..85f8f09 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -104,6 +104,6 @@
<!-- When split shade is used, this panel should be aligned to the top -->
<dimen name="qs_detail_margin_top">0dp</dimen>
- <!-- Internet panel related dimensions -->
- <dimen name="internet_dialog_list_max_width">624dp</dimen>
+ <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
+ <dimen name="large_dialog_width">624dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index 436f8d0..ac44251 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -22,5 +22,14 @@
<resources>
<integer name="status_bar_config_maxNotificationIcons">5</integer>
+ <!-- The maximum number of tiles in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_tiles">6</integer>
+
+ <!-- The number of columns in the QuickSettings -->
+ <integer name="quick_settings_num_columns">3</integer>
+
+ <!-- The maximum number of rows in the QuickSettings -->
+ <integer name="quick_settings_max_rows">3</integer>
+
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 01abf2a..8cc4c8d 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -513,7 +513,7 @@
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> மூலம் ரெக்கார்டிங் செய்யவோ அனுப்புவதற்கோ தொடங்கிவீட்டீர்களா?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"மீண்டும் காட்டாதே"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"எல்லாவற்றையும் அழி"</string>
- <string name="manage_notifications_text" msgid="6885645344647733116">"அறிவிப்புகளை நிர்வகி"</string>
+ <string name="manage_notifications_text" msgid="6885645344647733116">"நிர்வகி"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"இதுவரை வந்த அறிவிப்புகள்"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"புதிது"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"சைலன்ட்"</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"நெட்வொர்க்குகளைப் பார்க்க அன்லாக் செய்யுங்கள்"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"நெட்வொர்க்குகளைத் தேடுகிறது…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"தற்போதைக்கு வைஃபை தானாக இணைக்கப்படாது"</string>
<string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"நெட்வொர்க்குகளை மாற்ற ஈதர்நெட் இணைப்பைத் துண்டிக்கவும்"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 21a0ca9..dda2830 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -125,7 +125,7 @@
<string name="screenrecord_start_error" msgid="2200660692479682368">"స్క్రీన్ రికార్డింగ్ ప్రారంభించడంలో ఎర్రర్ ఏర్పడింది"</string>
<string name="usb_preference_title" msgid="1439924437558480718">"USB ఫైల్ బదిలీ ఎంపికలు"</string>
<string name="use_mtp_button_title" msgid="5036082897886518086">"మీడియా ప్లేయర్గా (MTP) మౌంట్ చేయి"</string>
- <string name="use_ptp_button_title" msgid="7676427598943446826">"కెమెరాగా (PTP) మౌంట్ చేయి"</string>
+ <string name="use_ptp_button_title" msgid="7676427598943446826">"ఒక కెమెరాగా (PTP) మౌంట్ చేయండి"</string>
<string name="installer_cd_button_title" msgid="5499998592841984743">"Macకు Android ఫైల్ బదిలీ యాప్ ఇన్స్టాల్ చేయండి"</string>
<string name="accessibility_back" msgid="6530104400086152611">"వెనుకకు"</string>
<string name="accessibility_home" msgid="5430449841237966217">"హోమ్"</string>
@@ -147,7 +147,7 @@
<string name="accessibility_manage_notification" msgid="582215815790143983">"నోటిఫికేషన్లను నిర్వహించండి"</string>
<string name="phone_label" msgid="5715229948920451352">"ఫోన్ను తెరువు"</string>
<string name="voice_assist_label" msgid="3725967093735929020">"వాయిస్ అసిస్టెంట్ను తెరువు"</string>
- <string name="camera_label" msgid="8253821920931143699">"కెమెరాను తెరువు"</string>
+ <string name="camera_label" msgid="8253821920931143699">"కెమెరాను తెరవండి"</string>
<string name="cancel" msgid="1089011503403416730">"రద్దు చేయి"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"నిర్ధారించు"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"మళ్లీ ప్రయత్నించు"</string>
@@ -430,11 +430,11 @@
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ప్రారంభించు"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ఆపు"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్ను అన్బ్లాక్ చేయమంటారా?"</string>
- <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరం కెమెరాను అన్బ్లాక్ చేయమంటారా?"</string>
- <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరం కెమెరా, మైక్రోఫోన్ను అన్బ్లాక్ చేయమంటారా?"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్బ్లాక్ చేయమంటారా?"</string>
+ <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్లను అన్బ్లాక్ చేయమంటారా?"</string>
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"మీ మైక్రోఫోన్ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్లు, సర్వీస్లకు యాక్సెస్ను ఇది అన్బ్లాక్ చేస్తుంది."</string>
- <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"మీ కెమెరాను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్లు, సర్వీస్లకు యాక్సెస్ను ఇది అన్బ్లాక్ చేస్తుంది."</string>
- <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"మీ కెమెరాను లేదా మైక్రోఫోన్ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్లు, సర్వీస్లకు యాక్సెస్ను ఇది అన్బ్లాక్ చేస్తుంది."</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ఇది, మీ కెమెరాను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్లకు, సర్వీస్లకు యాక్సెస్ను అన్బ్లాక్ చేస్తుంది."</string>
+ <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ఇది, మీ కెమెరాను లేదా మైక్రోఫోన్ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్లకు, సర్వీస్లకు యాక్సెస్ను అన్బ్లాక్ చేస్తుంది."</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"ఇతర పరికరం"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ఛార్జ్ చేయబడింది"</string>
@@ -462,7 +462,7 @@
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>కు చెందినది"</string>
<string name="do_financed_disclosure_with_name" msgid="6723004643314467864">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ద్వారా అందించబడింది"</string>
<string name="phone_hint" msgid="6682125338461375925">"ఫోన్ కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string>
- <string name="voice_hint" msgid="7476017460191291417">"వాయిస్ అసిస్టెంట్ చిహ్నం నుండి స్వైప్"</string>
+ <string name="voice_hint" msgid="7476017460191291417">"వాయిస్ అసిస్టెంట్ కోసం చిహ్నం నుండి స్వైప్ చేయండి"</string>
<string name="camera_hint" msgid="4519495795000658637">"కెమెరా కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string>
<string name="interruption_level_none_with_warning" msgid="8394434073508145437">"మొత్తం నిశ్శబ్దం. దీని వలన స్క్రీన్ రీడర్లు కూడా నిశ్శబ్దమవుతాయి."</string>
<string name="interruption_level_none" msgid="219484038314193379">"మొత్తం నిశ్శబ్దం"</string>
@@ -756,13 +756,13 @@
<string name="notification_delegate_header" msgid="1264510071031479920">"ప్రాక్సీ చేయబడిన నోటిఫికేషన్"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"అన్ని <xliff:g id="APP_NAME">%1$s</xliff:g> నోటిఫికేషన్లు"</string>
<string name="see_more_title" msgid="7409317011708185729">"మరిన్ని చూడండి"</string>
- <string name="appops_camera" msgid="5215967620896725715">"ఈ యాప్ ఈ కెమెరాను ఉపయోగిస్తోంది."</string>
+ <string name="appops_camera" msgid="5215967620896725715">"ఈ యాప్, కెమెరాను ఉపయోగిస్తోంది."</string>
<string name="appops_microphone" msgid="8805468338613070149">"ఈ యాప్ మైక్రోఫోన్ను ఉపయోగిస్తుంది."</string>
<string name="appops_overlay" msgid="4822261562576558490">"ఈ యాప్ మీ స్క్రీన్లోని ఇతర యాప్లపై ప్రదర్శించబడుతోంది."</string>
- <string name="appops_camera_mic" msgid="7032239823944420431">"ఈ యాప్ మైక్రోఫోన్ మరియు కెమెరాను ఉపయోగిస్తుంది."</string>
- <string name="appops_camera_overlay" msgid="6466845606058816484">"ఈ యాప్ మీ స్క్రీన్లోని ఇతర యాప్లపై ప్రదర్శించబడుతోంది మరియు కెమెరాను ఉపయోగిస్తుంది."</string>
+ <string name="appops_camera_mic" msgid="7032239823944420431">"ఈ యాప్ మైక్రోఫోన్ను, కెమెరాను ఉపయోగిస్తోంది."</string>
+ <string name="appops_camera_overlay" msgid="6466845606058816484">"ఈ యాప్ మీ స్క్రీన్లోని ఇతర యాప్లపై ప్రదర్శించబడుతోంది, కెమెరాను ఉపయోగిస్తోంది."</string>
<string name="appops_mic_overlay" msgid="4609326508944233061">"ఈ యాప్ మీ స్క్రీన్లోని ఇతర యాప్లపై ప్రదర్శించబడుతోంది మరియు మైక్రోఫోన్ను ఉపయోగిస్తుంది."</string>
- <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ఈ యాప్ మీ స్క్రీన్లోని ఇతర యాప్లపై ప్రదర్శించబడుతోంది మరియు మైక్రోఫోన్, కెమెరాను ఉపయోగిస్తుంది."</string>
+ <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ఈ యాప్ మీ స్క్రీన్లోని ఇతర యాప్లపై ప్రదర్శించబడుతోంది, మైక్రోఫోన్, కెమెరాను ఉపయోగిస్తోంది."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"సెట్టింగ్లు"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"సరే"</string>
<string name="feedback_alerted" msgid="5192459808484271208">"ఈ నోటిఫికేషన్, సిస్టమ్ ద్వారా దానంతట అదే <b>ఆటోమేటిక్గా ప్రమోట్ చేయబడింది</b>."</string>
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"నెట్వర్క్లను చూడటానికి అన్లాక్ చేయండి"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"నెట్వర్క్ల కోసం సెర్చ్ చేస్తోంది…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"నెట్వర్క్కు కనెక్ట్ చేయడం విఫలమైంది"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ప్రస్తుతానికి Wi-Fi ఆటోమేటిక్గా కనెక్ట్ అవ్వదు"</string>
<string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"నెట్వర్క్లను మార్చడానికి, ఈథర్నెట్ను డిస్కనెక్ట్ చేయండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings_tv.xml b/packages/SystemUI/res/values-te/strings_tv.xml
index 7eded1e..592f8ce 100644
--- a/packages/SystemUI/res/values-te/strings_tv.xml
+++ b/packages/SystemUI/res/values-te/strings_tv.xml
@@ -28,8 +28,8 @@
<string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"నోటిఫికేషన్లు లేవు"</string>
<string name="mic_recording_announcement" msgid="7587123608060316575">"మైక్రోఫోన్ రికార్డింగ్ చేస్తోంది"</string>
<string name="camera_recording_announcement" msgid="7240177719403759112">"కెమెరా రికార్డింగ్ చేస్తోంది"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"కెమెరా, మైక్రోఫోన్ రికార్డింగ్ చేస్తున్నాయి"</string>
+ <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"కెమెరా, మైక్రోఫోన్లు రికార్డింగ్ చేస్తున్నాయి"</string>
<string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"మైక్రోఫోన్ రికార్డింగ్ చేయడం ఆపివేసింది"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"కెమెరా రికార్డింగ్ చేయడం ఆపివేసింది"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"కెమెరా, మైక్రోఫోన్ రికార్డింగ్ చేయడం ఆపివేశాయి"</string>
+ <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"రికార్డింగ్ చేయడాన్ని కెమెరా ఆపివేసింది"</string>
+ <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"కెమెరా, మైక్రోఫోన్లు రికార్డింగ్ చేయడం ఆపివేశాయి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e053541..91a2440 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ปลดล็อกเพื่อดูเครือข่าย"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"กำลังค้นหาเครือข่าย…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"เชื่อมต่อเครือข่ายไม่สำเร็จ"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi จะไม่เชื่อมต่ออัตโนมัติในตอนนี้"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ตัดการเชื่อมต่ออีเทอร์เน็ตเพื่อสลับเครือข่าย"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9eb1449..dcde0fc 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"I-unlock para tingnan ang mga network"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Naghahanap ng mga network…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Hind nakakonekta sa network"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Hindi awtomatikong kokonekta ang Wi-Fi sa ngayon"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para lumipat ng network, idiskonekta ang ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d1599bf..367d0bf 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ağları görmek için kilidi açın"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ağlar aranıyor…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ağa bağlanılamadı"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Şu anda kablosuz ağa otomatik olarak bağlanılamıyor"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ağ değiştirmek için ethernet bağlantısını kesin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 482742a..e692a36 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1187,5 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Розблокувати, щоб переглянути мережі"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Пошук мереж…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не вдалося підключитися до мережі"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Пристрій не підключатиметься до Wi-Fi автоматично"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Щоб вибрати іншу мережу, від’єднайте кабель Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 9c80c7e..da431826 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1175,5 +1175,8 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"نیٹ ورکس کو دیکھنے کے لیے غیر مقفل کریں"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string>
+ <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
+ <skip />
<string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"نیٹ ورکس پر سوئچ کرنے کیلئے، ایتھرنیٹ غیر منسلک کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3b77577..f0afb6c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Tarmoqlarni koʻrish uchun qulfdan chiqaring"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Tarmoqlar qidirilmoqda…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Tarmoqqa ulanmadi"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hozir avtomatik ulanmaydi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Boshqa tarmoqqa almashish uchun Ethernet tarmogʻini uzing"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 2d8d2de..b0f1dcf 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Mở khóa để xem mạng"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Đang tìm mạng…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Không kết nối được với mạng"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Tạm thời, Wi-Fi sẽ không tự động kết nối"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Để chuyển mạng, hãy rút cáp Ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 41ebca9..7caae5d 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"解锁即可查看网络"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜索网络…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"未能连接到网络"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WLAN 暂时无法自动连接"</string>
<string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切换网络,请断开以太网连接"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 63bc3f6..4a8567d 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"解鎖即可查看網絡"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網絡…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連接網絡"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前系統不會自動連線至 Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網絡,請中斷以太網連線"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index eeb7522..b444d42 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"解鎖螢幕即可查看網路"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網路…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連上網路"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前不會自動連上 Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網路,請中斷乙太網路連線"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 29ae369..7988476 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1175,5 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Vula ukuze ubuke amanethiwekhi"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iseshela amanethiwekhi…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yehlulekile ukuxhuma kunethiwekhi"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"I-Wi-Fi ngeke ixhume ngokuzenzakalelayo okwamanje"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
+ <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ukuze ushintshe amanethiwekhi, nqamula i-ethernet"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 08778bf..03c6fdd 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -70,7 +70,7 @@
<color name="keyguard_shadow_color">#B2000000</color>
<!-- Color for the images in keyguard number pad buttons -->
- <color name="keyguard_keypad_image_color">@android:color/background_light</color>
+ <color name="keyguard_keypad_image_color">?android:attr/textColorPrimaryInverse</color>
<!-- Color for rounded background for activated user in keyguard user switcher -->
<color name="kg_user_switcher_activated_background_color">#26000000</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 32b85fe..8650654 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1279,7 +1279,7 @@
<dimen name="ongoing_appops_dialog_circle_size">32dp</dimen>
- <dimen name="ongoing_appops_dialog_icon_size">20dp</dimen>
+ <dimen name="ongoing_appops_dialog_icon_size">16dp</dimen>
<dimen name="ongoing_appops_dialog_side_padding">16dp</dimen>
@@ -1310,6 +1310,7 @@
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
<dimen name="qs_aa_media_rec_album_size_collapsed">72dp</dimen>
<dimen name="qs_aa_media_rec_album_size_expanded">76dp</dimen>
+ <dimen name="qs_aa_media_gradient_bg_width">32dp</dimen>
<dimen name="qs_aa_media_rec_album_margin">8dp</dimen>
<dimen name="qs_aa_media_rec_album_margin_vert">4dp</dimen>
<dimen name="qq_aa_media_rec_header_text_size">16sp</dimen>
@@ -1595,7 +1596,9 @@
<!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_margin">12dp</dimen>
<dimen name="internet_dialog_list_max_height">646dp</dimen>
- <dimen name="internet_dialog_list_max_width">@dimen/match_parent</dimen>
+
+ <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
+ <dimen name="large_dialog_width">@dimen/match_parent</dimen>
<!-- Signal icon in internet dialog -->
<dimen name="signal_strength_icon_size">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 03ba28a..c26de37 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3020,6 +3020,10 @@
<string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string>
<!-- Provider Model: Failure notification for connect -->
<string name="wifi_failed_connect_message">Failed to connect to network</string>
+ <!-- Provider Model: Toast message for when the user selects cellular as the internet provider and Wi-Fi auto-connect is temporarily disabled [CHAR LIMIT=60]-->
+ <string name="wifi_wont_autoconnect_for_now">Wi\u2011Fi won\u2019t auto-connect for now</string>
<!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
<string name="see_all_networks">See all</string>
+ <!-- Summary for warning to disconnect ethernet first then switch to other networks. [CHAR LIMIT=60] -->
+ <string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d254742..6594216 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -331,9 +331,6 @@
<style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast">
</style>
- <style name="Animation.MediaOutputDialog" parent="@android:style/Animation.InputMethod">
- </style>
-
<!-- Standard animations for hiding and showing the status bar. -->
<style name="Animation.StatusBar">
</style>
@@ -416,7 +413,9 @@
</style>
<!-- Overridden by values-television/styles.xml with tv-specific settings -->
- <style name="volume_dialog_theme" parent="Theme.SystemUI"/>
+ <style name="volume_dialog_theme" parent="Theme.SystemUI">
+ <item name="android:windowIsFloating">true</item>
+ </style>
<style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog" />
@@ -434,10 +433,6 @@
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
- <style name="Theme.SystemUI.Dialog.MediaOutput">
- <item name="android:windowBackground">@drawable/media_output_dialog_background</item>
- </style>
-
<style name="QSBorderlessButton">
<item name="android:padding">12dp</item>
<item name="android:background">@drawable/qs_btn_borderless_rect</item>
@@ -951,4 +946,60 @@
<item name="android:maxHeight">4dp</item>
</style>
+ <!-- Internet Dialog -->
+ <style name="InternetDialog">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center_vertical|start</item>
+ <item name="android:layout_marginStart">@dimen/internet_dialog_network_layout_margin</item>
+ </style>
+
+ <style name="InternetDialog.Network">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">88dp</item>
+ <item name="android:layout_marginEnd">@dimen/internet_dialog_network_layout_margin</item>
+ <item name="android:paddingStart">22dp</item>
+ <item name="android:paddingEnd">22dp</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:focusable">true</item>
+ <item name="android:clickable">true</item>
+ </style>
+
+ <style name="InternetDialog.NetworkTitle">
+ <item name="android:layout_marginEnd">7dp</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:textAppearance">@style/TextAppearance.InternetDialog</item>
+ </style>
+
+ <style name="InternetDialog.NetworkTitle.Active">
+ <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Active</item>
+ </style>
+
+ <style name="InternetDialog.NetworkSummary">
+ <item name="android:layout_marginEnd">34dp</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary</item>
+ </style>
+
+ <style name="InternetDialog.NetworkSummary.Active">
+ <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary.Active
+ </item>
+ </style>
+
+ <style name="TextAppearance.InternetDialog">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textDirection">locale</item>
+ </style>
+
+ <style name="TextAppearance.InternetDialog.Secondary">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.InternetDialog.Active"/>
+
+ <style name="TextAppearance.InternetDialog.Secondary.Active"/>
+
</resources>
diff --git a/packages/SystemUI/res/xml/media_recommendation_collapsed.xml b/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
index 5c41ad8..b6258d1 100644
--- a/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
@@ -19,25 +19,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<Constraint
- android:id="@+id/recommendation_card_icon"
- android:layout_width="@dimen/qs_media_icon_size"
- android:layout_height="@dimen/qs_media_icon_size"
- android:layout_marginTop="@dimen/qs_media_padding"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
- app:layout_constraintHorizontal_bias="0" />
-
- <Constraint
- android:id="@+id/recommendation_card_text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/recommendation_card_icon"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
- app:layout_constraintHorizontal_bias="0" />
-
- <Constraint
android:id="@+id/media_cover1_container"
android:layout_width="0dp"
android:layout_height="@dimen/qs_aa_media_rec_album_size_collapsed"
diff --git a/packages/SystemUI/res/xml/media_recommendation_expanded.xml b/packages/SystemUI/res/xml/media_recommendation_expanded.xml
index 8a3d5ca..2fb3341 100644
--- a/packages/SystemUI/res/xml/media_recommendation_expanded.xml
+++ b/packages/SystemUI/res/xml/media_recommendation_expanded.xml
@@ -19,25 +19,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<Constraint
- android:id="@+id/recommendation_card_icon"
- android:layout_width="@dimen/qs_media_icon_size"
- android:layout_height="@dimen/qs_media_icon_size"
- android:layout_marginTop="@dimen/qs_media_padding"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
- app:layout_constraintHorizontal_bias="0" />
-
- <Constraint
- android:id="@+id/recommendation_card_text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/recommendation_card_icon"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
- app:layout_constraintHorizontal_bias="0" />
-
- <Constraint
android:id="@+id/media_cover1_container"
android:layout_width="0dp"
android:layout_height="@dimen/qs_aa_media_rec_album_size_expanded"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index e33985d..6154d84 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -18,16 +18,19 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
+import android.annotation.TargetApi;
import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.Surface;
+import android.view.WindowManager;
/* Common code */
public class Utilities {
@@ -117,21 +120,20 @@
return hints;
}
- /** See {@link #isTablet(Configuration, Context)} */
+ /** @return whether or not {@param context} represents that of a large screen device or not */
+ @TargetApi(Build.VERSION_CODES.R)
public static boolean isTablet(Context context) {
- Configuration newConfig = context.getResources().getConfiguration();
- return isTablet(newConfig, context);
+ final WindowManager windowManager = context.getSystemService(WindowManager.class);
+ final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
+
+ float originalSmallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
+ context.getResources().getConfiguration().densityDpi);
+ return dpiFromPx(Math.min(bounds.width(), bounds.height()), DENSITY_DEVICE_STABLE)
+ >= TABLET_MIN_DPS && originalSmallestWidth >= TABLET_MIN_DPS;
}
- /**
- * @return whether or not {@param newConfig} represents that of a large screen device or not
- */
- public static boolean isTablet(Configuration newConfig, Context context) {
- float density = Resources.getSystem().getDisplayMetrics().density;
- int size = Math.min((int) (density * newConfig.screenWidthDp),
- (int) (density* newConfig.screenHeightDp));
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
- return (size / densityRatio) >= TABLET_MIN_DPS;
+ public static float dpiFromPx(float size, int densityDpi) {
+ float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ return (size / densityRatio);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 92f8454..0529cdbc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -208,8 +208,7 @@
mView.post(() -> {
if (mView.isShown()) {
mPasswordEntry.requestFocus();
- mInputMethodManager.showSoftInput(
- mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ mPasswordEntry.getWindowInsetsController().show(WindowInsets.Type.ime());
}
});
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 371564a..ef4353b 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -48,6 +48,7 @@
private ImageView mBgView;
private int mLockIconColor;
+ private boolean mUseBackground = false;
public LockIconView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -61,8 +62,8 @@
mBgView = findViewById(R.id.lock_icon_bg);
}
- void updateColorAndBackgroundVisibility(boolean useBackground) {
- if (useBackground && mLockIcon.getDrawable() != null) {
+ void updateColorAndBackgroundVisibility() {
+ if (mUseBackground && mLockIcon.getDrawable() != null) {
mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
@@ -78,6 +79,9 @@
void setImageDrawable(Drawable drawable) {
mLockIcon.setImageDrawable(drawable);
+
+ if (!mUseBackground) return;
+
if (drawable == null) {
mBgView.setVisibility(View.INVISIBLE);
} else {
@@ -86,6 +90,14 @@
}
/**
+ * Whether or not to render the lock icon background. Mainly used for UDPFS.
+ */
+ public void setUseBackground(boolean useBackground) {
+ mUseBackground = useBackground;
+ updateColorAndBackgroundVisibility();
+ }
+
+ /**
* Set the location of the lock icon.
*/
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 28e19ac..8cfd225 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -346,7 +346,7 @@
}
private void updateColors() {
- mView.updateColorAndBackgroundVisibility(mUdfpsSupported);
+ mView.updateColorAndBackgroundVisibility();
}
private void updateConfiguration() {
@@ -427,6 +427,8 @@
boolean wasUdfpsEnrolled = mUdfpsEnrolled;
mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+ mView.setUseBackground(mUdfpsSupported);
+
mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
updateVisibility();
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index c659bf7..f4ce643 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.VectorDrawable;
@@ -26,8 +27,6 @@
import androidx.annotation.Nullable;
-import com.android.systemui.R;
-
/**
* Similar to the {@link NumPadKey}, but displays an image.
*/
@@ -92,7 +91,10 @@
public void reloadColors() {
if (mAnimator != null) mAnimator.reloadColors(getContext());
- int imageColor = getContext().getColor(R.color.keyguard_keypad_image_color);
+ int[] customAttrs = {android.R.attr.textColorPrimaryInverse};
+ TypedArray a = getContext().obtainStyledAttributes(customAttrs);
+ int imageColor = a.getColor(0, 0);
+ a.recycle();
((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 05256e6..f182e77 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -41,6 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Prefs;
+import com.android.systemui.shared.system.SysUiStatsLog;
/**
* Contains logic for an accessibility floating menu view.
@@ -177,6 +178,9 @@
}
private void onDragEnd(Position position) {
+ SysUiStatsLog.write(SysUiStatsLog.ACCESSIBILITY_FLOATING_MENU_UI_CHANGED,
+ position.getPercentageX(), position.getPercentageY(),
+ mContext.getResources().getConfiguration().orientation);
savePosition(mContext, position);
showDockTooltipIfNecessary(mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index 41c7ebe..b7398d8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -17,6 +17,8 @@
import android.content.Context
import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
@@ -34,6 +36,8 @@
import android.view.WindowManager
import androidx.annotation.RawRes
import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.model.KeyPath
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
@@ -140,6 +144,7 @@
windowManager.updateViewLayout(overlayView, overlayViewParams)
}
}
+ lottie.addOverlayDynamicColor(context)
return view
}
@@ -194,3 +199,21 @@
private fun Display.isPortrait(): Boolean =
rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
+
+private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
+ fun update() {
+ val c = context.getColor(R.color.biometric_dialog_accent)
+ for (key in listOf(".blue600", ".blue400")) {
+ addValueCallback(
+ KeyPath(key, "**"),
+ LottieProperty.COLOR_FILTER
+ ) { PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP) }
+ }
+ }
+
+ if (composition != null) {
+ update()
+ } else {
+ addLottieOnCompositionLoadedListener { update() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index db5dbb0..c743fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -772,7 +772,8 @@
31, // MEDIA_RESUME
uid,
interactedSubcardRank,
- interactedSubcardCardinality
+ interactedSubcardCardinality,
+ 0 // received_latency_millis
)
/* ktlint-disable max-line-length */
}
@@ -857,7 +858,8 @@
) {
shouldPrioritizeSs = shouldPrioritize
removeMediaPlayer(key)
- val sortKey = MediaSortKey(isSsMediaRec = true, EMPTY, clock.currentTimeMillis())
+ val sortKey = MediaSortKey(/* isSsMediaRec= */ true,
+ EMPTY.copy(isPlaying = false), clock.currentTimeMillis())
mediaData.put(key, sortKey)
mediaPlayers.put(sortKey, player)
smartspaceMediaData = data
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c125612..424f801 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -75,6 +75,8 @@
private static final String TAG = "MediaControlPanel";
private static final float DISABLED_ALPHA = 0.38f;
+ private static final String EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = "com.google"
+ + ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity";
private static final String EXTRAS_SMARTSPACE_INTENT =
"com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT";
private static final int MEDIA_RECOMMENDATION_ITEMS_PER_ROW = 3;
@@ -252,7 +254,8 @@
openGuts();
return true;
} else {
- return false;
+ closeGuts();
+ return true;
}
});
mRecommendationViewHolder.getCancel().setOnClickListener(v -> {
@@ -361,9 +364,9 @@
seamlessView.setVisibility(View.VISIBLE);
setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */);
setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */);
- seamlessView.setOnClickListener(v -> {
- mMediaOutputDialogFactory.create(data.getPackageName(), true);
- });
+ seamlessView.setOnClickListener(
+ v -> mMediaOutputDialogFactory.create(data.getPackageName(), true,
+ mPlayerViewHolder.getSeamlessButton()));
ImageView iconView = mPlayerViewHolder.getSeamlessIcon();
TextView deviceName = mPlayerViewHolder.getSeamlessText();
@@ -585,6 +588,14 @@
ViewGroup mediaCoverContainer = mediaCoverContainers.get(uiComponentIndex);
setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation,
uiComponentIndex);
+ // Bubble up the long-click event to the card.
+ mediaCoverContainer.setOnLongClickListener(v -> {
+ View parent = (View) v.getParent();
+ if (parent != null) {
+ parent.performLongClick();
+ }
+ return true;
+ });
// Set up the accessibility label for the media item.
String artistName = recommendation.getExtras()
@@ -627,6 +638,22 @@
closeGuts();
mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
data.getTargetId(), MediaViewController.GUTS_ANIMATION_DURATION + 100L);
+
+ Intent dismissIntent = data.getDismissIntent();
+ if (dismissIntent == null) {
+ Log.w(TAG, "Cannot create dismiss action click action: "
+ + "extras missing dismiss_intent.");
+ return;
+ }
+
+ if (dismissIntent.getComponent() != null
+ && dismissIntent.getComponent().getClassName()
+ .equals(EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME)) {
+ // Dismiss the card Smartspace data through Smartspace trampoline activity.
+ mContext.startActivity(dismissIntent);
+ } else {
+ mContext.sendBroadcast(dismissIntent);
+ }
});
mController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index c8deb01..79206e8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.content.Context
import android.os.SystemProperties
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
@@ -32,6 +33,8 @@
private const val TAG = "MediaDataFilter"
private const val DEBUG = true
+private const val EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = ("com.google" +
+ ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity")
private const val RESUMABLE_MEDIA_MAX_AGE_SECONDS_KEY = "resumable_media_max_age_seconds"
/**
@@ -51,6 +54,7 @@
* background users (e.g. timeouts).
*/
class MediaDataFilter @Inject constructor(
+ private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
private val mediaResumeListener: MediaResumeListener,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -229,6 +233,18 @@
mediaDataManager.setTimedOut(it, timedOut = true, forceUpdate = true)
}
if (smartspaceMediaData.isActive) {
+ val dismissIntent = smartspaceMediaData.dismissIntent
+ if (dismissIntent == null) {
+ Log.w(TAG, "Cannot create dismiss action click action: " +
+ "extras missing dismiss_intent.")
+ } else if (dismissIntent.getComponent() != null &&
+ dismissIntent.getComponent().getClassName()
+ == EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME) {
+ // Dismiss the card Smartspace data through Smartspace trampoline activity.
+ context.startActivity(dismissIntent)
+ } else {
+ context.sendBroadcast(dismissIntent)
+ }
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = smartspaceMediaData.targetId, isValid = smartspaceMediaData.isValid)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index ba99f5d..06a1eea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -76,12 +76,13 @@
private const val TAG = "MediaDataManager"
private const val DEBUG = true
+private const val EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY = "dismiss_intent"
private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
emptyList(), emptyList(), "INVALID", null, null, null, true, null)
@VisibleForTesting
internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false,
- "INVALID", null, emptyList(), 0)
+ "INVALID", null, emptyList(), null, 0)
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
if (!sbn.notification.hasMediaSession()) {
@@ -883,12 +884,22 @@
target: SmartspaceTarget,
isActive: Boolean
): SmartspaceMediaData {
+ var dismissIntent: Intent? = null
+ if (target.baseAction != null && target.baseAction.extras != null) {
+ dismissIntent = target
+ .baseAction
+ .extras
+ .getParcelable(EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY) as Intent?
+ }
packageName(target)?.let {
return SmartspaceMediaData(target.smartspaceTargetId, isActive, true, it,
- target.baseAction, target.iconGrid, 0)
+ target.baseAction, target.iconGrid,
+ dismissIntent, 0)
}
return EMPTY_SMARTSPACE_MEDIA_DATA
- .copy(targetId = target.smartspaceTargetId, isActive = isActive)
+ .copy(targetId = target.smartspaceTargetId,
+ isActive = isActive,
+ dismissIntent = dismissIntent)
}
private fun packageName(target: SmartspaceTarget): String? {
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 35603b6..f32dad6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -43,6 +43,7 @@
val seamless = itemView.requireViewById<ViewGroup>(R.id.media_seamless)
val seamlessIcon = itemView.requireViewById<ImageView>(R.id.media_seamless_image)
val seamlessText = itemView.requireViewById<TextView>(R.id.media_seamless_text)
+ val seamlessButton = itemView.requireViewById<View>(R.id.media_seamless_button)
// Seek bar
val seekBar = itemView.requireViewById<SeekBar>(R.id.media_progress_bar)
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
index 9ac1289..61fdefd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media
import android.app.smartspace.SmartspaceAction
+import android.content.Intent
/** State of a Smartspace media recommendations view. */
data class SmartspaceMediaData(
@@ -45,6 +46,10 @@
*/
val recommendations: List<SmartspaceAction>,
/**
+ * Intent for the user's initiated dismissal.
+ */
+ val dismissIntent: Intent?,
+ /**
* View's background color.
*/
val backgroundColor: Int
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
index b6c2ef1..140a1fe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
@@ -1,10 +1,13 @@
package com.android.systemui.media
import android.app.smartspace.SmartspaceTarget
+import android.util.Log
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import javax.inject.Inject
+private const val TAG = "SsMediaDataProvider"
+
/** Provides SmartspaceTargets of media types for SystemUI media control. */
class SmartspaceMediaDataProvider @Inject constructor() : BcSmartspaceDataPlugin {
@@ -31,6 +34,10 @@
}
}
+ if (!mediaTargets.isEmpty()) {
+ Log.d(TAG, "Forwarding Smartspace media updates $mediaTargets")
+ }
+
smartspaceMediaTargets = mediaTargets
smartspaceMediaTargetListeners.forEach {
it.onSmartspaceTargetsUpdated(smartspaceMediaTargets)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 391dff63..d1b6548 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -45,11 +45,14 @@
private static final String TAG = "MediaOutputAdapter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final MediaOutputDialog mMediaOutputDialog;
private ViewGroup mConnectedItem;
private boolean mIncludeDynamicGroup;
- public MediaOutputAdapter(MediaOutputController controller) {
+ public MediaOutputAdapter(MediaOutputController controller,
+ MediaOutputDialog mediaOutputDialog) {
super(controller);
+ mMediaOutputDialog = mediaOutputDialog;
}
@Override
@@ -136,7 +139,7 @@
mDivider.setTransitionAlpha(1);
mAddIcon.setVisibility(View.VISIBLE);
mAddIcon.setTransitionAlpha(1);
- mAddIcon.setOnClickListener(v -> onEndItemClick());
+ mAddIcon.setOnClickListener(this::onEndItemClick);
} else {
// Init non-active device layout
mDivider.setVisibility(View.GONE);
@@ -197,7 +200,7 @@
mDivider.setTransitionAlpha(1);
mAddIcon.setVisibility(View.VISIBLE);
mAddIcon.setTransitionAlpha(1);
- mAddIcon.setOnClickListener(v -> onEndItemClick());
+ mAddIcon.setOnClickListener(this::onEndItemClick);
} else {
mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
@@ -232,8 +235,8 @@
}
}
- private void onEndItemClick() {
- mController.launchMediaOutputGroupDialog();
+ private void onEndItemClick(View view) {
+ mController.launchMediaOutputGroupDialog(mMediaOutputDialog.getDialogView());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index cdcdf9a..85d0802 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -82,7 +82,7 @@
};
public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
- super(context, R.style.Theme_SystemUI_Dialog_MediaOutput);
+ super(context);
mContext = context;
mMediaOutputController = mediaOutputController;
mLayoutManager = new LinearLayoutManager(mContext);
@@ -97,15 +97,15 @@
mDialogView = LayoutInflater.from(mContext).inflate(R.layout.media_output_dialog, null);
final Window window = getWindow();
final WindowManager.LayoutParams lp = window.getAttributes();
- lp.gravity = Gravity.BOTTOM;
+ lp.gravity = Gravity.CENTER;
// Config insets to make sure the layout is above the navigation bar
lp.setFitInsetsTypes(statusBars() | navigationBars());
lp.setFitInsetsSides(WindowInsets.Side.all());
lp.setFitInsetsIgnoringVisibility(true);
window.setAttributes(lp);
window.setContentView(mDialogView);
- window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- window.setWindowAnimations(R.style.Animation_MediaOutputDialog);
+ window.setLayout(mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width),
+ ViewGroup.LayoutParams.WRAP_CONTENT);
mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
@@ -229,4 +229,8 @@
void onHeaderIconClick() {
}
+
+ View getDialogView() {
+ return mDialogView;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index b2def7a..437a0c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -32,6 +32,7 @@
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -48,6 +49,7 @@
import com.android.settingslib.media.MediaOutputConstants;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -73,6 +75,7 @@
private final MediaSessionManager mMediaSessionManager;
private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
private final NotificationEntryManager mNotificationEntryManager;
@@ -82,6 +85,7 @@
private MediaController mMediaController;
@VisibleForTesting
Callback mCallback;
+ Callback mPreviousCallback;
@VisibleForTesting
LocalMediaManager mLocalMediaManager;
@@ -92,7 +96,8 @@
public MediaOutputController(@NonNull Context context, String packageName,
boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
lbm, ShadeController shadeController, ActivityStarter starter,
- NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger) {
+ NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger,
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
@@ -104,6 +109,7 @@
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mUiEventLogger = uiEventLogger;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
}
void start(@NonNull Callback cb) {
@@ -129,7 +135,19 @@
}
return;
}
+
+ if (mPreviousCallback != null) {
+ Log.w(TAG,
+ "Callback started when mPreviousCallback is not null, which is unexpected");
+ mPreviousCallback.dismissDialog();
+ }
+
+ // If we start the output group dialog when the output dialog is shown, we need to keep a
+ // reference to the output dialog to set it back as the callback once we dismiss the output
+ // group dialog.
+ mPreviousCallback = mCallback;
mCallback = cb;
+
mLocalMediaManager.unregisterCallback(this);
mLocalMediaManager.stopScan();
mLocalMediaManager.registerCallback(this);
@@ -145,6 +163,15 @@
mLocalMediaManager.stopScan();
}
mMediaDevices.clear();
+
+ // If there was a previous callback, i.e. we just dismissed the output group dialog and are
+ // now back on the output dialog, then we reset the callback to its previous value.
+ mCallback = null;
+ Callback previous = mPreviousCallback;
+ mPreviousCallback = null;
+ if (previous != null) {
+ start(previous);
+ }
}
@Override
@@ -436,6 +463,10 @@
}
void launchBluetoothPairing() {
+ // Dismissing a dialog into its touch surface and starting an activity at the same time
+ // looks bad, so let's make sure the dialog just fades out quickly.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
+
mCallback.dismissDialog();
final ActivityStarter.OnDismissAction postKeyguardAction = () -> {
mContext.sendBroadcast(new Intent()
@@ -447,14 +478,10 @@
mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
}
- void launchMediaOutputDialog() {
- mCallback.dismissDialog();
- new MediaOutputDialog(mContext, mAboveStatusbar, this, mUiEventLogger);
- }
-
- void launchMediaOutputGroupDialog() {
- mCallback.dismissDialog();
- new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+ void launchMediaOutputGroupDialog(View mediaOutputDialog) {
+ // We show the output group dialog from the output dialog.
+ MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+ mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
}
boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 53029bd0..eca8ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -40,11 +40,10 @@
mediaOutputController, UiEventLogger uiEventLogger) {
super(context, mediaOutputController);
mUiEventLogger = uiEventLogger;
- mAdapter = new MediaOutputAdapter(mMediaOutputController);
+ mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
if (!aboveStatusbar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
- show();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 0f340a5..b91901d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -18,8 +18,10 @@
import android.content.Context
import android.media.session.MediaSessionManager
+import android.view.View
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.phone.ShadeController
@@ -35,19 +37,29 @@
private val shadeController: ShadeController,
private val starter: ActivityStarter,
private val notificationEntryManager: NotificationEntryManager,
- private val uiEventLogger: UiEventLogger
+ private val uiEventLogger: UiEventLogger,
+ private val dialogLaunchAnimator: DialogLaunchAnimator
) {
companion object {
var mediaOutputDialog: MediaOutputDialog? = null
}
/** Creates a [MediaOutputDialog] for the given package. */
- fun create(packageName: String, aboveStatusBar: Boolean) {
+ fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) {
+ // Dismiss the previous dialog, if any.
mediaOutputDialog?.dismiss()
- mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar,
- mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
- uiEventLogger).run {
- MediaOutputDialog(context, aboveStatusBar, this, uiEventLogger)
+
+ val controller = MediaOutputController(context, packageName, aboveStatusBar,
+ mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
+ uiEventLogger, dialogLaunchAnimator)
+ val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
+ mediaOutputDialog = dialog
+
+ // Show the dialog.
+ if (view != null) {
+ dialogLaunchAnimator.showFromView(dialog, view)
+ } else {
+ dialog.show()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index 4079304..1300400 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -38,7 +38,6 @@
if (!aboveStatusbar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
- show();
}
@Override
@@ -83,6 +82,8 @@
@Override
void onHeaderIconClick() {
- mMediaOutputController.launchMediaOutputDialog();
+ // Given that we launched the media output group dialog from the media output dialog,
+ // dismissing this dialog will show the media output dialog again.
+ dismiss();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index b8df49e..a1a630a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -205,7 +205,7 @@
@Override
public void onConfigChanged(Configuration newConfig) {
boolean isOldConfigTablet = mIsTablet;
- mIsTablet = isTablet(newConfig, mContext);
+ mIsTablet = isTablet(mContext);
boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
// If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
if (largeScreenChanged && updateNavbarForTaskbar()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 1bd3664..1a7a306 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -114,6 +114,7 @@
super.onConfigurationChanged(newConfig);
if (mLayoutOrientation != newConfig.orientation) {
mLayoutOrientation = newConfig.orientation;
+ mDistributeTiles = true;
setCurrentItem(0, false);
mPageToRestore = 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 31ddd97..8e43661 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Point;
+import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
@@ -304,6 +305,16 @@
}
}
+ @Override
+ protected boolean isTransformedTouchPointInView(float x, float y,
+ View child, PointF outLocalPoint) {
+ // Prevent touches outside the clipped area from propagating to a child in that area.
+ if (mClippingEnabled && y + getTranslationY() > mFancyClippingTop) {
+ return false;
+ }
+ return super.isTransformedTouchPointInView(x, y, child, outLocalPoint);
+ }
+
private void updateClippingPath() {
mFancyClippingPath.reset();
if (!mClippingEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 97568f9..f7d1b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -26,6 +26,7 @@
import android.metrics.LogMaker;
import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
@@ -79,7 +80,8 @@
private final QSHost.Callback mQSHostCallback = this::setTiles;
- private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
+ @VisibleForTesting
+ protected final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
new QSPanel.OnConfigurationChangedListener() {
@Override
public void onConfigurationChange(Configuration newConfig) {
@@ -156,6 +158,7 @@
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
mHost.addCallback(mQSHostCallback);
setTiles();
+ mLastOrientation = getResources().getConfiguration().orientation;
switchTileLayout(true);
mDumpManager.registerDumpable(mView.getDumpableTag(), this);
@@ -356,8 +359,7 @@
return false;
}
return mUsingMediaPlayer && mMediaHost.getVisible()
- && getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
+ && mLastOrientation == Configuration.ORIENTATION_LANDSCAPE;
}
private void logTiles() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 73d1370..14e0f70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -54,7 +54,7 @@
private var lastAlarmInfo: AlarmManager.AlarmClockInfo? = null
private val icon = ResourceIcon.get(R.drawable.ic_alarm)
@VisibleForTesting
- internal val defaultIntent = Intent(AlarmClock.ACTION_SET_ALARM)
+ internal val defaultIntent = Intent(AlarmClock.ACTION_SHOW_ALARMS)
private val callback = NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
lastAlarmInfo = nextAlarm
refreshState()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 8e886e8..b1af841 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -275,7 +275,7 @@
return;
}
mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
- mInfo.dataContentDescription = indicators.description != null
+ mInfo.dataContentDescription = indicators.qsDescription != null
? indicators.typeContentDescriptionHtml : null;
mInfo.activityIn = indicators.activityIn;
mInfo.activityOut = indicators.activityOut;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 34f2b63..87edc2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -47,6 +47,7 @@
/** Quick settings tile: Hotspot **/
public class HotspotTile extends QSTileImpl<BooleanState> {
+
private final Icon mEnabledStatic = ResourceIcon.get(R.drawable.ic_hotspot);
private final HotspotController mHotspotController;
@@ -98,7 +99,7 @@
@Override
public Intent getLongClickIntent() {
- return new Intent(Settings.ACTION_TETHER_SETTINGS);
+ return new Intent(Settings.ACTION_WIFI_TETHER_SETTING);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index cc9e748..530804e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -279,9 +279,9 @@
// Not data sim, don't display.
return;
}
- mCellularInfo.mDataSubscriptionName = indicators.description == null
- ? mController.getMobileDataNetworkName() : indicators.description;
- mCellularInfo.mDataContentDescription = indicators.description != null
+ mCellularInfo.mDataSubscriptionName = indicators.qsDescription == null
+ ? mController.getMobileDataNetworkName() : indicators.qsDescription;
+ mCellularInfo.mDataContentDescription = indicators.qsDescription != null
? indicators.typeContentDescriptionHtml : null;
mCellularInfo.mMobileSignalIconId = indicators.qsIcon.icon;
mCellularInfo.mQsTypeIcon = indicators.qsType;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index b1db8a9..11430d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -45,13 +45,14 @@
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
-import android.widget.Button;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Switch;
import android.widget.TextView;
+import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
@@ -60,7 +61,6 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.settingslib.Utils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -105,12 +105,10 @@
private View mDivider;
private ProgressBar mProgressBar;
private LinearLayout mInternetDialogLayout;
- private LinearLayout mInternetListLayout;
private LinearLayout mConnectedWifListLayout;
- private LinearLayout mConnectedWifList;
private LinearLayout mMobileNetworkLayout;
- private LinearLayout mMobileNetworkList;
private LinearLayout mTurnWifiOnLayout;
+ private LinearLayout mEthernetLayout;
private TextView mWifiToggleTitleText;
private LinearLayout mSeeAllLayout;
private RecyclerView mWifiRecyclerView;
@@ -123,10 +121,9 @@
private TextView mMobileSummaryText;
private Switch mMobileDataToggle;
private Switch mWiFiToggle;
- private Button mDoneButton;
+ private FrameLayout mDoneLayout;
private Drawable mBackgroundOn;
private int mListMaxHeight;
- private int mLayoutWidth;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mCanConfigMobileData;
@@ -181,8 +178,6 @@
};
mListMaxHeight = context.getResources().getDimensionPixelSize(
R.dimen.internet_dialog_list_max_height);
- mLayoutWidth = context.getResources().getDimensionPixelSize(
- R.dimen.internet_dialog_list_max_width);
mUiEventLogger = uiEventLogger;
mAdapter = new InternetAdapter(mInternetDialogController);
if (!aboveStatusBar) {
@@ -211,7 +206,7 @@
window.setContentView(mDialogView);
//Only fix the width for large screen or tablet.
window.setLayout(mContext.getResources().getDimensionPixelSize(
- R.dimen.internet_dialog_list_max_width), ViewGroup.LayoutParams.WRAP_CONTENT);
+ R.dimen.large_dialog_width), ViewGroup.LayoutParams.WRAP_CONTENT);
window.setWindowAnimations(R.style.Animation_InternetDialog);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
window.addFlags(FLAG_LAYOUT_NO_LIMITS);
@@ -221,20 +216,18 @@
mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
mDivider = mDialogView.requireViewById(R.id.divider);
mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
- mInternetListLayout = mDialogView.requireViewById(R.id.internet_list);
+ mEthernetLayout = mDialogView.requireViewById(R.id.ethernet_layout);
mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
- mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
- mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
mConnectedWifiSummaryText = mDialogView.requireViewById(R.id.wifi_connected_summary);
mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
- mDoneButton = mDialogView.requireViewById(R.id.done);
+ mDoneLayout = mDialogView.requireViewById(R.id.done_layout);
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
@@ -286,7 +279,7 @@
mConnectedWifListLayout.setOnClickListener(null);
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
- mDoneButton.setOnClickListener(null);
+ mDoneLayout.setOnClickListener(null);
mInternetDialogController.onStop();
mInternetDialogFactory.destroyDialog();
}
@@ -309,7 +302,9 @@
} else {
mInternetDialogSubTitle.setText(getSubtitleText());
}
- setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular());
+ updateEthernet();
+ setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular()
+ || mInternetDialogController.isCarrierNetworkActive());
if (!mCanConfigWifi) {
return;
@@ -352,10 +347,16 @@
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
});
- mDoneButton.setOnClickListener(v -> dismiss());
+ mDoneLayout.setOnClickListener(v -> dismiss());
}
- private void setMobileDataLayout(boolean isCellularNetwork) {
+ @MainThread
+ private void updateEthernet() {
+ mEthernetLayout.setVisibility(
+ mInternetDialogController.hasEthernet() ? View.VISIBLE : View.GONE);
+ }
+
+ private void setMobileDataLayout(boolean isCarrierNetworkConnected) {
if (mInternetDialogController.isAirplaneModeEnabled()
|| !mInternetDialogController.hasCarrier()) {
mMobileNetworkLayout.setVisibility(View.GONE);
@@ -371,38 +372,33 @@
mMobileSummaryText.setVisibility(View.GONE);
}
mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
- if (mInternetDialogController.isNightMode()) {
- int titleColor = isCellularNetwork ? mContext.getColor(
- R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
- mContext, android.R.attr.textColorPrimary);
- int summaryColor = isCellularNetwork ? mContext.getColor(
- R.color.connected_network_secondary_color) : Utils.getColorAttrDefaultColor(
- mContext, android.R.attr.textColorSecondary);
-
- mMobileTitleText.setTextColor(titleColor);
- mMobileSummaryText.setTextColor(summaryColor);
- }
- mMobileNetworkLayout.setBackground(isCellularNetwork ? mBackgroundOn : null);
+ mMobileTitleText.setTextAppearance(isCarrierNetworkConnected
+ ? R.style.TextAppearance_InternetDialog_Active
+ : R.style.TextAppearance_InternetDialog);
+ mMobileSummaryText.setTextAppearance(isCarrierNetworkConnected
+ ? R.style.TextAppearance_InternetDialog_Secondary_Active
+ : R.style.TextAppearance_InternetDialog_Secondary);
+ mMobileNetworkLayout.setBackground(isCarrierNetworkConnected ? mBackgroundOn : null);
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
}
}
+ @MainThread
private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
mWiFiToggle.setChecked(isWifiEnabled);
- if (isDeviceLocked && mInternetDialogController.isNightMode()) {
- int titleColor = mConnectedWifiEntry != null ? mContext.getColor(
- R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
- mContext, android.R.attr.textColorPrimary);
- mWifiToggleTitleText.setTextColor(titleColor);
+ if (isDeviceLocked) {
+ mWifiToggleTitleText.setTextAppearance((mConnectedWifiEntry != null)
+ ? R.style.TextAppearance_InternetDialog_Active
+ : R.style.TextAppearance_InternetDialog);
}
mTurnWifiOnLayout.setBackground(
(isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
}
+ @MainThread
private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
if (!isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
- mConnectedWifListLayout.setBackground(null);
mConnectedWifListLayout.setVisibility(View.GONE);
return;
}
@@ -411,15 +407,8 @@
mConnectedWifiSummaryText.setText(mConnectedWifiEntry.getSummary(false));
mConnectedWifiIcon.setImageDrawable(
mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
- if (mInternetDialogController.isNightMode()) {
- mConnectedWifiTitleText.setTextColor(
- mContext.getColor(R.color.connected_network_primary_color));
- mConnectedWifiSummaryText.setTextColor(
- mContext.getColor(R.color.connected_network_secondary_color));
- }
mWifiSettingsIcon.setColorFilter(
mContext.getColor(R.color.connected_network_primary_color));
- mConnectedWifListLayout.setBackground(mBackgroundOn);
}
void onClickConnectedWifi() {
@@ -528,11 +517,18 @@
}
@Override
+ @WorkerThread
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
mHandler.post(() -> updateDialog());
}
@Override
+ @WorkerThread
+ public void onLost(Network network) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
public void onSubscriptionsChanged(int defaultDataSubId) {
mDefaultDataSubId = defaultDataSubId;
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 50e7e43..aaba5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -19,21 +19,24 @@
import static com.android.settingslib.mobile.MobileMappings.getIconKey;
import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
import android.os.Handler;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
@@ -47,8 +50,10 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
-import android.widget.Toast;
+import android.view.View;
+import android.view.WindowManager;
+import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -67,11 +72,15 @@
import com.android.settingslib.wifi.WifiUtils;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.toast.SystemUIToast;
+import com.android.systemui.toast.ToastFactory;
+import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
@@ -117,10 +126,12 @@
private SubscriptionManager mSubscriptionManager;
private TelephonyManager mTelephonyManager;
private ConnectivityManager mConnectivityManager;
+ private CarrierConfigTracker mCarrierConfigTracker;
private TelephonyDisplayInfo mTelephonyDisplayInfo =
new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
private Handler mHandler;
+ private Handler mWorkerHandler;
private MobileMappings.Config mConfig = null;
private Executor mExecutor;
private AccessPointController mAccessPointController;
@@ -133,8 +144,17 @@
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private GlobalSettings mGlobalSettings;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback;
+ private WindowManager mWindowManager;
+ private ToastFactory mToastFactory;
@VisibleForTesting
+ static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
+ @VisibleForTesting
+ static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f;
+ @VisibleForTesting
+ static final long SHORT_DURATION_TIMEOUT = 4000;
+ @VisibleForTesting
protected ActivityStarter mActivityStarter;
@VisibleForTesting
protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
@@ -146,6 +166,8 @@
protected boolean mCanConfigWifi;
@VisibleForTesting
protected KeyguardStateController mKeyguardStateController;
+ @VisibleForTesting
+ protected boolean mHasEthernet = false;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -171,11 +193,15 @@
@Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
@Main Handler handler, @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
- GlobalSettings globalSettings, KeyguardStateController keyguardStateController) {
+ GlobalSettings globalSettings, KeyguardStateController keyguardStateController,
+ WindowManager windowManager, ToastFactory toastFactory,
+ @Background Handler workerHandler,
+ CarrierConfigTracker carrierConfigTracker) {
if (DEBUG) {
Log.d(TAG, "Init InternetDialogController");
}
mHandler = handler;
+ mWorkerHandler = workerHandler;
mExecutor = mainExecutor;
mContext = context;
mGlobalSettings = globalSettings;
@@ -183,16 +209,20 @@
mTelephonyManager = telephonyManager;
mConnectivityManager = connectivityManager;
mSubscriptionManager = subscriptionManager;
+ mCarrierConfigTracker = carrierConfigTracker;
mBroadcastDispatcher = broadcastDispatcher;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardStateController = keyguardStateController;
mConnectionStateFilter = new IntentFilter();
mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ mConnectionStateFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
mUiEventLogger = uiEventLogger;
mActivityStarter = starter;
mAccessPointController = accessPointController;
- mConfig = MobileMappings.Config.readConfig(mContext);
mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
+ mConnectivityManagerNetworkCallback = new DataConnectivityListener();
+ mWindowManager = windowManager;
+ mToastFactory = toastFactory;
}
void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -212,13 +242,12 @@
if (DEBUG) {
Log.d(TAG, "Init, SubId: " + mDefaultDataSubId);
}
+ mConfig = MobileMappings.Config.readConfig(mContext);
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
mInternetTelephonyCallback = new InternetTelephonyCallback();
mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
// Listen the connectivity changes
- mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build(), new DataConnectivityListener(), mHandler);
+ mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback);
mCanConfigWifi = canConfigWifi;
scanWifiAccessPoints();
}
@@ -233,6 +262,7 @@
mOnSubscriptionsChangedListener);
mAccessPointController.removeAccessPointCallback(this);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
}
@VisibleForTesting
@@ -351,11 +381,6 @@
return drawable;
}
- boolean isNightMode() {
- return (mContext.getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
- }
-
Drawable getSignalStrengthDrawable() {
Drawable drawable = mContext.getDrawable(
R.drawable.ic_signal_strength_zero_bar_no_internet);
@@ -373,9 +398,12 @@
drawable = shared.get();
}
- drawable.setTint(activeNetworkIsCellular() ? mContext.getColor(
- R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
- mContext, android.R.attr.textColorTertiary));
+ int tintColor = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.textColorTertiary);
+ if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
+ tintColor = mContext.getColor(R.color.connected_network_primary_color);
+ }
+ drawable.setTint(tintColor);
} catch (Throwable e) {
e.printStackTrace();
}
@@ -518,7 +546,7 @@
String getMobileNetworkSummary() {
String description = getNetworkTypeDescription(mContext, mConfig,
mTelephonyDisplayInfo, mDefaultDataSubId);
- return getMobileSummary(mContext, mTelephonyManager, description);
+ return getMobileSummary(mContext, description);
}
/**
@@ -536,9 +564,7 @@
}
int resId = mapIconSets(config).get(iconKey).dataContentDescription;
- final MergedCarrierEntry mergedCarrierEntry =
- mAccessPointController.getMergedCarrierEntry();
- if (mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork()) {
+ if (isCarrierNetworkActive()) {
SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
TelephonyIcons.CARRIER_MERGED_WIFI;
resId = carrierMergedWifiIconGroup.dataContentDescription;
@@ -548,8 +574,7 @@
? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
}
- private String getMobileSummary(Context context, TelephonyManager telephonyManager,
- String networkTypeDescription) {
+ private String getMobileSummary(Context context, String networkTypeDescription) {
if (!isMobileDataEnabled()) {
return context.getString(R.string.mobile_data_off_summary);
}
@@ -557,7 +582,7 @@
return context.getString(R.string.mobile_data_no_connection);
}
String summary = networkTypeDescription;
- if (activeNetworkIsCellular()) {
+ if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
summary = context.getString(R.string.preference_summary_default_combination,
context.getString(R.string.mobile_data_connection_active),
networkTypeDescription);
@@ -582,10 +607,35 @@
final MergedCarrierEntry mergedCarrierEntry =
mAccessPointController.getMergedCarrierEntry();
if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
- mergedCarrierEntry.connect(null /* ConnectCallback */);
+ mergedCarrierEntry.connect(null /* ConnectCallback */, false);
+ makeOverlayToast(R.string.wifi_wont_autoconnect_for_now);
}
}
+ boolean isCarrierNetworkActive() {
+ final MergedCarrierEntry mergedCarrierEntry =
+ mAccessPointController.getMergedCarrierEntry();
+ return mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork();
+ }
+
+ @WorkerThread
+ void setMergedCarrierWifiEnabledIfNeed(int subId, boolean enabled) {
+ // If the Carrier Provisions Wi-Fi Merged Networks enabled, do not set the merged carrier
+ // Wi-Fi state together.
+ if (mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(subId)) {
+ return;
+ }
+
+ final MergedCarrierEntry entry = mAccessPointController.getMergedCarrierEntry();
+ if (entry == null) {
+ if (DEBUG) {
+ Log.d(TAG, "MergedCarrierEntry is null, can not set the status.");
+ }
+ return;
+ }
+ entry.setEnabled(enabled);
+ }
+
WifiManager getWifiManager() {
return mWifiManager;
}
@@ -660,6 +710,7 @@
}
}
}
+ mWorkerHandler.post(() -> setMergedCarrierWifiEnabledIfNeed(subId, enabled));
}
boolean isDataStateInService() {
@@ -725,20 +776,20 @@
Log.d(TAG, "connect to unsaved network " + ap.getTitle());
}
}
- ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap));
+ ap.connect(new WifiEntryConnectCallback(mActivityStarter, ap, this));
return false;
}
static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
final ActivityStarter mActivityStarter;
- final Context mContext;
final WifiEntry mWifiEntry;
+ final InternetDialogController mInternetDialogController;
- WifiEntryConnectCallback(ActivityStarter activityStarter, Context context,
- WifiEntry connectWifiEntry) {
+ WifiEntryConnectCallback(ActivityStarter activityStarter, WifiEntry connectWifiEntry,
+ InternetDialogController internetDialogController) {
mActivityStarter = activityStarter;
- mContext = context;
mWifiEntry = connectWifiEntry;
+ mInternetDialogController = internetDialogController;
}
@Override
@@ -753,8 +804,7 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startActivity(intent, false /* dismissShade */);
} else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
- Toast.makeText(mContext, R.string.wifi_failed_connect_message,
- Toast.LENGTH_SHORT).show();
+ mInternetDialogController.makeOverlayToast(R.string.wifi_failed_connect_message);
} else {
if (DEBUG) {
Log.d(TAG, "connect failure reason=" + status);
@@ -800,6 +850,9 @@
}
int count = MAX_WIFI_ENTRY_COUNT;
+ if (mHasEthernet) {
+ count -= 1;
+ }
if (hasCarrier()) {
count -= 1;
}
@@ -870,31 +923,43 @@
@Override
@WorkerThread
public void onCapabilitiesChanged(@NonNull Network network,
- @NonNull NetworkCapabilities networkCapabilities) {
- if (mCanConfigWifi) {
- for (int transport : networkCapabilities.getTransportTypes()) {
- if (transport == NetworkCapabilities.TRANSPORT_WIFI) {
- scanWifiAccessPoints();
- break;
- }
- }
+ @NonNull NetworkCapabilities capabilities) {
+ mHasEthernet = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
+ if (mCanConfigWifi && (mHasEthernet || capabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_WIFI))) {
+ scanWifiAccessPoints();
}
- final Network activeNetwork = mConnectivityManager.getActiveNetwork();
- if (activeNetwork != null && activeNetwork.equals(network)) {
- // update UI
- mCallback.onCapabilitiesChanged(network, networkCapabilities);
- }
+ // update UI
+ mCallback.onCapabilitiesChanged(network, capabilities);
}
+
+ @Override
+ @WorkerThread
+ public void onLost(@NonNull Network network) {
+ mHasEthernet = false;
+ mCallback.onLost(network);
+ }
+ }
+
+ /**
+ * Return {@code true} If the Ethernet exists
+ */
+ @MainThread
+ public boolean hasEthernet() {
+ return mHasEthernet;
}
private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+ if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
if (DEBUG) {
Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED");
}
+ mConfig = MobileMappings.Config.readConfig(context);
+ updateListener();
+ } else if (WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.equals(action)) {
updateListener();
}
}
@@ -934,6 +999,8 @@
void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
+ void onLost(@NonNull Network network);
+
void onSubscriptionsChanged(int defaultDataSubId);
void onServiceStateChanged(ServiceState serviceState);
@@ -949,4 +1016,57 @@
void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
@Nullable WifiEntry connectedEntry);
}
+
+ void makeOverlayToast(int stringId) {
+ final Resources res = mContext.getResources();
+
+ final SystemUIToast systemUIToast = mToastFactory.createToast(mContext,
+ res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(),
+ res.getConfiguration().orientation);
+ if (systemUIToast == null) {
+ return;
+ }
+
+ View toastView = systemUIToast.getView();
+
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.format = PixelFormat.TRANSLUCENT;
+ params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+ params.y = systemUIToast.getYOffset();
+
+ int absGravity = Gravity.getAbsoluteGravity(systemUIToast.getGravity(),
+ res.getConfiguration().getLayoutDirection());
+ params.gravity = absGravity;
+ if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+ params.horizontalWeight = TOAST_PARAMS_HORIZONTAL_WEIGHT;
+ }
+ if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+ params.verticalWeight = TOAST_PARAMS_VERTICAL_WEIGHT;
+ }
+
+ mWindowManager.addView(toastView, params);
+
+ Animator inAnimator = systemUIToast.getInAnimation();
+ if (inAnimator != null) {
+ inAnimator.start();
+ }
+
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ Animator outAnimator = systemUIToast.getOutAnimation();
+ if (outAnimator != null) {
+ outAnimator.start();
+ outAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mWindowManager.removeViewImmediate(toastView);
+ }
+ });
+ }
+ }
+ }, SHORT_DURATION_TIMEOUT);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 26781f4..2133cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -29,8 +29,8 @@
import android.graphics.Bitmap;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
+import android.media.MediaCodec;
import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.MediaRecorder;
@@ -187,77 +187,63 @@
* @param refreshRate Desired refresh rate
* @return array with supported width, height, and refresh rate
*/
- private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate) {
- double maxScale = 0;
+ private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate)
+ throws IOException {
+ String videoType = MediaFormat.MIMETYPE_VIDEO_AVC;
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- MediaCodecInfo.VideoCapabilities maxInfo = null;
- for (MediaCodecInfo codec : codecList.getCodecInfos()) {
- String videoType = MediaFormat.MIMETYPE_VIDEO_AVC;
- String[] types = codec.getSupportedTypes();
- for (String t : types) {
- if (!t.equalsIgnoreCase(videoType)) {
- continue;
- }
- MediaCodecInfo.CodecCapabilities capabilities =
- codec.getCapabilitiesForType(videoType);
- if (capabilities != null && capabilities.getVideoCapabilities() != null) {
- MediaCodecInfo.VideoCapabilities vc = capabilities.getVideoCapabilities();
+ // Get max size from the decoder, to ensure recordings will be playable on device
+ MediaCodec decoder = MediaCodec.createDecoderByType(videoType);
+ MediaCodecInfo.VideoCapabilities vc = decoder.getCodecInfo()
+ .getCapabilitiesForType(videoType).getVideoCapabilities();
+ decoder.release();
- int width = vc.getSupportedWidths().getUpper();
- int height = vc.getSupportedHeights().getUpper();
+ // Check if we can support screen size as-is
+ int width = vc.getSupportedWidths().getUpper();
+ int height = vc.getSupportedHeights().getUpper();
- int screenWidthAligned = screenWidth;
- if (screenWidthAligned % vc.getWidthAlignment() != 0) {
- screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment());
- }
- int screenHeightAligned = screenHeight;
- if (screenHeightAligned % vc.getHeightAlignment() != 0) {
- screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment());
- }
+ int screenWidthAligned = screenWidth;
+ if (screenWidthAligned % vc.getWidthAlignment() != 0) {
+ screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment());
+ }
+ int screenHeightAligned = screenHeight;
+ if (screenHeightAligned % vc.getHeightAlignment() != 0) {
+ screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment());
+ }
- if (width >= screenWidthAligned && height >= screenHeightAligned
- && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) {
- // Desired size is supported, now get the rate
- int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned,
- screenHeightAligned).getUpper().intValue();
+ if (width >= screenWidthAligned && height >= screenHeightAligned
+ && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) {
+ // Desired size is supported, now get the rate
+ int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned,
+ screenHeightAligned).getUpper().intValue();
- if (maxRate < refreshRate) {
- refreshRate = maxRate;
- }
- Log.d(TAG, "Screen size supported at rate " + refreshRate);
- return new int[]{screenWidthAligned, screenHeightAligned, refreshRate};
- }
-
- // Otherwise, continue searching
- double scale = Math.min(((double) width / screenWidth),
- ((double) height / screenHeight));
- if (scale > maxScale) {
- maxScale = Math.min(1, scale);
- maxInfo = vc;
- }
- }
+ if (maxRate < refreshRate) {
+ refreshRate = maxRate;
}
+ Log.d(TAG, "Screen size supported at rate " + refreshRate);
+ return new int[]{screenWidthAligned, screenHeightAligned, refreshRate};
}
- // Resize for max supported size
- int scaledWidth = (int) (screenWidth * maxScale);
- int scaledHeight = (int) (screenHeight * maxScale);
- if (scaledWidth % maxInfo.getWidthAlignment() != 0) {
- scaledWidth -= (scaledWidth % maxInfo.getWidthAlignment());
+ // Otherwise, resize for max supported size
+ double scale = Math.min(((double) width / screenWidth),
+ ((double) height / screenHeight));
+
+ int scaledWidth = (int) (screenWidth * scale);
+ int scaledHeight = (int) (screenHeight * scale);
+ if (scaledWidth % vc.getWidthAlignment() != 0) {
+ scaledWidth -= (scaledWidth % vc.getWidthAlignment());
}
- if (scaledHeight % maxInfo.getHeightAlignment() != 0) {
- scaledHeight -= (scaledHeight % maxInfo.getHeightAlignment());
+ if (scaledHeight % vc.getHeightAlignment() != 0) {
+ scaledHeight -= (scaledHeight % vc.getHeightAlignment());
}
// Find max supported rate for size
- int maxRate = maxInfo.getSupportedFrameRatesFor(scaledWidth, scaledHeight)
+ int maxRate = vc.getSupportedFrameRatesFor(scaledWidth, scaledHeight)
.getUpper().intValue();
if (maxRate < refreshRate) {
refreshRate = maxRate;
}
- Log.d(TAG, "Resized by " + maxScale + ": " + scaledWidth + ", " + scaledHeight
+ Log.d(TAG, "Resized by " + scale + ": " + scaledWidth + ", " + scaledHeight
+ ", " + refreshRate);
return new int[]{scaledWidth, scaledHeight, refreshRate};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 17bcfe7..8a39719 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -335,7 +335,7 @@
info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
}
}
- if (info != null) {
+ if (!TextUtils.isEmpty(info)) {
mRotateTextViewController.updateIndication(
INDICATION_TYPE_OWNER_INFO,
new KeyguardIndication.Builder()
@@ -433,7 +433,7 @@
}
private void updateResting() {
- if (mRestingIndication != null
+ if (!TextUtils.isEmpty(mRestingIndication)
&& !mRotateTextViewController.hasIndications()) {
mRotateTextViewController.updateIndication(
INDICATION_TYPE_RESTING,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 94f186f..ea00d92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -22,6 +22,9 @@
import android.os.Handler;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -62,6 +65,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
@@ -261,4 +265,29 @@
@Binds
QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
+
+ /**
+ */
+ @Provides
+ @SysUISingleton
+ static LaunchAnimator provideLaunchAnimator(Context context) {
+ return new LaunchAnimator(context);
+ }
+
+ /**
+ */
+ @Provides
+ @SysUISingleton
+ static ActivityLaunchAnimator provideActivityLaunchAnimator(LaunchAnimator launchAnimator) {
+ return new ActivityLaunchAnimator(launchAnimator);
+ }
+
+ /**
+ */
+ @Provides
+ @SysUISingleton
+ static DialogLaunchAnimator provideDialogLaunchAnimator(Context context,
+ LaunchAnimator launchAnimator) {
+ return new DialogLaunchAnimator(context, launchAnimator, new SystemUIHostDialogProvider());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index d7f3225..a76389b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -24,7 +24,6 @@
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
-import android.content.pm.UserInfo
import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
@@ -193,7 +192,7 @@
}
private fun connectSession() {
- if (plugin == null || session != null) {
+ if (plugin == null || session != null || smartspaceViews.isEmpty()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
index f19cf5d..64a7305 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -2,8 +2,8 @@
import android.util.MathUtils
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Interpolators
+import com.android.systemui.animation.LaunchAnimator
import kotlin.math.min
/** Parameters for the notifications expand animations. */
@@ -15,7 +15,7 @@
topCornerRadius: Float = 0f,
bottomCornerRadius: Float = 0f
-) : ActivityLaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) {
+) : LaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) {
@VisibleForTesting
constructor() : this(
top = 0, bottom = 0, left = 0, right = 0, topCornerRadius = 0f, bottomCornerRadius = 0f
@@ -55,6 +55,6 @@
}
fun getProgress(delay: Long, duration: Long): Float {
- return ActivityLaunchAnimator.getProgress(linearProgress, delay, duration)
+ return LaunchAnimator.getProgress(linearProgress, delay, duration)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index a9ad000..60f44a0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -305,9 +305,6 @@
NotificationEntry entry = mPendingNotifications.get(key);
entry.abortTask();
mPendingNotifications.remove(key);
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryCleanUp(entry);
- }
mLogger.logInflationAborted(key, "pending", reason);
}
NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
@@ -477,6 +474,18 @@
if (!lifetimeExtended) {
// At this point, we are guaranteed the notification will be removed
abortExistingInflation(key, "removeNotification");
+ // Fix for b/201097913: NotifCollectionListener#onEntryRemoved specifies that
+ // #onEntryRemoved should be called when a notification is cancelled,
+ // regardless of whether the notification was pending or active.
+ // Note that mNotificationEntryListeners are NOT notified of #onEntryRemoved
+ // because for that interface, #onEntryRemoved should only be called for
+ // active entries, NOT pending ones.
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryRemoved(pendingEntry, REASON_UNKNOWN);
+ }
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryCleanUp(pendingEntry);
+ }
mAllNotifications.remove(pendingEntry);
mLeakDetector.trackGarbage(pendingEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 1bbef25..22c3eda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -3,6 +3,7 @@
import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.LaunchAnimator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
@@ -54,7 +55,7 @@
// Do nothing. Notifications are always animated inside their rootView.
}
- override fun createAnimatorState(): ActivityLaunchAnimator.State {
+ override fun createAnimatorState(): LaunchAnimator.State {
// If the notification panel is collapsed, the clip may be larger than the height.
val height = max(0, notification.actualHeight - notification.clipBottomAmount)
val location = notification.locationOnScreen
@@ -72,12 +73,12 @@
notification.currentBackgroundRadiusTop
}
val params = ExpandAnimationParameters(
- top = windowTop,
- bottom = location[1] + height,
- left = location[0],
- right = location[0] + notification.width,
- topCornerRadius = topCornerRadius,
- bottomCornerRadius = notification.currentBackgroundRadiusBottom
+ top = windowTop,
+ bottom = location[1] + height,
+ left = location[0],
+ right = location[0] + notification.width,
+ topCornerRadius = topCornerRadius,
+ bottomCornerRadius = notification.currentBackgroundRadiusBottom
)
params.startTranslationZ = notification.translationZ
@@ -86,8 +87,8 @@
params.startClipTopAmount = notification.clipTopAmount
if (notification.isChildInGroup) {
params.startNotificationTop += notification.notificationParent.translationY
- val parentRoundedClip = Math.max(clipStartLocation
- - notification.notificationParent.locationOnScreen[1], 0)
+ val parentRoundedClip = Math.max(
+ clipStartLocation - notification.notificationParent.locationOnScreen[1], 0)
params.parentStartRoundedTopClipping = parentRoundedClip
val parentClip = notification.notificationParent.clipTopAmount
@@ -157,7 +158,7 @@
}
override fun onLaunchAnimationProgress(
- state: ActivityLaunchAnimator.State,
+ state: LaunchAnimator.State,
progress: Float,
linearProgress: Float
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7babcba..4400909 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -610,6 +610,7 @@
mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
mGroupMembershipManager = groupMembershipManager;
mGroupExpansionManager = groupExpansionManager;
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f85c749..75eb88f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -173,6 +173,7 @@
private final NotificationLockscreenUserManager mLockscreenUserManager;
// TODO: StatusBar should be encapsulated behind a Controller
private final StatusBar mStatusBar;
+ private final NotificationGroupManagerLegacy mLegacyGroupManager;
private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@@ -663,6 +664,8 @@
mStatusBar.requestNotificationUpdate("onGroupsChanged");
}
});
+ mLegacyGroupManager = featureFlags.isNewNotifPipelineRenderingEnabled()
+ ? null : legacyGroupManager;
mSilentHeaderController = silentHeaderController;
mFeatureFlags = featureFlags;
mNotifPipeline = notifPipeline;
@@ -1223,7 +1226,11 @@
final boolean inSection =
NotificationStackScrollLayout.matchesSelection(row, selection);
if (matchClearable && inSection) {
- return true;
+ if (mLegacyGroupManager == null
+ || !mLegacyGroupManager.isSummaryOfSuppressedGroup(
+ row.getEntry().getSbn())) {
+ return true;
+ }
}
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index b21088a..c88d920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -103,8 +103,8 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
@@ -212,7 +212,7 @@
*/
private static final int FLING_HIDE = 2;
private static final long ANIMATION_DELAY_ICON_FADE_IN =
- ActivityLaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
+ LaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
- CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
private final DozeParameters mDozeParameters;
@@ -1256,8 +1256,10 @@
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
+ boolean splitShadeWithActiveMedia =
+ mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia();
if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade)
- || (mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia())) {
+ || (splitShadeWithActiveMedia && !mDozing)) {
mKeyguardStatusViewController.displayClock(SMALL);
} else {
mKeyguardStatusViewController.displayClock(LARGE);
@@ -1319,7 +1321,8 @@
private void updateKeyguardStatusViewAlignment(boolean animate) {
boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
- boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications;
+ boolean shouldBeCentered =
+ !mShouldUseSplitNotificationShade || !hasVisibleNotifications || mDozing;
if (mStatusViewCentered != shouldBeCentered) {
mStatusViewCentered = shouldBeCentered;
ConstraintSet constraintSet = new ConstraintSet();
@@ -3594,7 +3597,7 @@
}
public void applyLaunchAnimationProgress(float linearProgress) {
- boolean hideIcons = ActivityLaunchAnimator.getProgress(linearProgress,
+ boolean hideIcons = LaunchAnimator.getProgress(linearProgress,
ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
if (hideIcons != mHideIconsDuringLaunchAnimation) {
mHideIconsDuringLaunchAnimation = hideIcons;
@@ -3778,6 +3781,9 @@
@Override
protected TouchHandler createTouchHandler() {
return new TouchHandler() {
+
+ private long mLastTouchDownTime = -1L;
+
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mBlockTouches || mQs.disallowPanelTouches()) {
@@ -3809,6 +3815,19 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (event.getDownTime() == mLastTouchDownTime) {
+ // An issue can occur when swiping down after unlock, where multiple down
+ // events are received in this handler with identical downTimes. Until the
+ // source of the issue can be located, detect this case and ignore.
+ // see b/193350347
+ Log.w(TAG, "Duplicate down event detected... ignoring");
+ return true;
+ }
+ mLastTouchDownTime = event.getDownTime();
+ }
+
+
if (mBlockTouches || (mQsFullyExpanded && mQs != null
&& mQs.disallowPanelTouches())) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index eca91a3..e775e96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -182,8 +182,7 @@
}
boolean fullyClosed = true;
boolean fullyOpened = false;
- if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
- PanelViewController pv = mPanel;
+ if (SPEW) LOG("panelExpansionChanged: start state=%d, f=%.1f", mState, frac);
mExpanded = expanded;
mPanelFraction = frac;
updateVisibility();
@@ -194,9 +193,7 @@
onPanelPeeked();
}
fullyClosed = false;
- final float thisFrac = pv.getExpandedFraction();
- if (SPEW) LOG("panelExpansionChanged: -> %s: f=%.1f", pv.getName(), thisFrac);
- fullyOpened = thisFrac >= 1f;
+ fullyOpened = frac >= 1f;
}
if (fullyOpened && !mTracking) {
go(STATE_OPEN);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index ee7725f..e104aef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -135,6 +135,7 @@
import com.android.systemui.SystemUI;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.biometrics.AuthRippleController;
@@ -669,7 +670,8 @@
private final SysuiStatusBarStateController mStatusBarStateController;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
- private ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
@@ -792,7 +794,9 @@
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
Optional<StartingSurface> startingSurfaceOptional,
TunerService tunerService,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ ActivityLaunchAnimator activityLaunchAnimator,
+ DialogLaunchAnimator dialogLaunchAnimator) {
super(context);
mNotificationsController = notificationsController;
mLightBarController = lightBarController;
@@ -900,6 +904,8 @@
});
mActivityIntentHelper = new ActivityIntentHelper(mContext);
+ mActivityLaunchAnimator = activityLaunchAnimator;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
// TODO(b/190746471): Find a better home for this.
DateTimeView.setReceiverHandler(timeTickHandler);
@@ -1467,7 +1473,7 @@
private void setUpPresenter() {
// Set up the initial notification state.
- mActivityLaunchAnimator = new ActivityLaunchAnimator(mKeyguardHandler, mContext);
+ mActivityLaunchAnimator.setCallback(mKeyguardHandler);
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
mNotificationShadeWindowViewController,
mStackScrollerController.getNotificationListContainer(),
@@ -2549,7 +2555,8 @@
animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch(
true /* isActivityIntent */);
ActivityLaunchAnimator.Controller animController =
- animate ? wrapAnimationController(animationController, dismissShade) : null;
+ animationController != null ? wrapAnimationController(animationController,
+ dismissShade) : null;
// If we animate, we will dismiss the shade only once the animation is done. This is taken
// care of by the StatusBarLaunchAnimationController.
@@ -4426,6 +4433,8 @@
&& !mBiometricUnlockController.isWakeAndUnlock()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
+
+ mDialogLaunchAnimator.onDozeAmountChanged(linear);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7725443..5bc97a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -53,6 +53,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -94,7 +95,8 @@
// with the appear animations of the PIN/pattern/password views.
private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320;
- private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200;
+ // The duration to fade the nav bar content in/out when the device starts to sleep
+ private static final long NAV_BAR_CONTENT_FADE_DURATION = 125;
// Duration of the Keyguard dismissal animation in case the user is currently locked. This is to
// make everything a bit slower to bridge a gap until the user is unlocked and home screen has
@@ -197,10 +199,8 @@
private boolean mLastGesturalNav;
private boolean mLastIsDocked;
private boolean mLastPulsing;
- private boolean mLastAnimatedToSleep;
private int mLastBiometricMode;
private boolean mQsExpanded;
- private boolean mAnimatedToSleep;
private OnDismissAction mAfterKeyguardGoneAction;
private Runnable mKeyguardGoneCancelAction;
@@ -325,20 +325,6 @@
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
}
- mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
- @Override
- public void onFinishedWakingUp() {
- mAnimatedToSleep = false;
- updateStates();
- }
-
- @Override
- public void onFinishedGoingToSleep() {
- mAnimatedToSleep =
- mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying();
- updateStates();
- }
- });
}
@Override
@@ -571,12 +557,26 @@
public void onStartedWakingUp() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(false);
+ View currentView = getCurrentNavBarView();
+ if (currentView != null) {
+ currentView.animate()
+ .alpha(1f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start();
+ }
}
@Override
public void onStartedGoingToSleep() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(true);
+ View currentView = getCurrentNavBarView();
+ if (currentView != null) {
+ currentView.animate()
+ .alpha(0f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start();
+ }
}
@Override
@@ -1013,10 +1013,28 @@
mLastBiometricMode = mBiometricUnlockController.getMode();
mLastGesturalNav = mGesturalNav;
mLastIsDocked = mIsDocked;
- mLastAnimatedToSleep = mAnimatedToSleep;
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
+ /**
+ * Updates the visibility of the nav bar content views.
+ */
+ private void updateNavigationBarContentVisibility(boolean navBarContentVisible) {
+ final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ if (navBarView != null && navBarView.getCurrentView() != null) {
+ final View currentView = navBarView.getCurrentView();
+ currentView.setVisibility(navBarContentVisible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ private View getCurrentNavBarView() {
+ final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ return navBarView != null ? navBarView.getCurrentView() : null;
+ }
+
+ /**
+ * Updates the visibility of the nav bar window (which will cause insets changes).
+ */
protected void updateNavigationBarVisibility(boolean navBarVisible) {
if (mStatusBar.getNavigationBarView() != null) {
if (navBarVisible) {
@@ -1044,7 +1062,7 @@
boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked)
&& mGesturalNav;
- return (!mAnimatedToSleep && !keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
+ return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
|| mRemoteInputActive || keyguardWithGestureNav
|| mGlobalActionsVisible);
}
@@ -1057,7 +1075,7 @@
boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing
|| mLastPulsing && !mLastIsDocked) && mLastGesturalNav;
- return (!mLastAnimatedToSleep && !keyguardShowing && !hideWhileDozing || mLastBouncerShowing
+ return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
|| mLastRemoteInputActive || keyguardWithGestureNav
|| mLastGlobalActionsVisible);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index 14e513a..32aae6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.phone
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.LaunchAnimator
/**
* A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right
@@ -22,7 +23,7 @@
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(true)
if (!isExpandingFullyAbove) {
- statusBar.collapsePanelWithDuration(ActivityLaunchAnimator.ANIMATION_DURATION.toInt())
+ statusBar.collapsePanelWithDuration(LaunchAnimator.ANIMATION_DURATION.toInt())
}
}
@@ -33,7 +34,7 @@
}
override fun onLaunchAnimationProgress(
- state: ActivityLaunchAnimator.State,
+ state: LaunchAnimator.State,
progress: Float,
linearProgress: Float
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 1e98c75..9415d50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -30,19 +30,24 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogListener;
+import com.android.systemui.animation.ListenableDialog;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
/**
* Base class for dialogs that should appear over panels and keyguard.
* The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
* and dismisses itself when it receives the broadcast.
*/
-public class SystemUIDialog extends AlertDialog {
-
+public class SystemUIDialog extends AlertDialog implements ListenableDialog {
private final Context mContext;
private final DismissReceiver mDismissReceiver;
+ private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>();
public SystemUIDialog(Context context) {
this(context, R.style.Theme_SystemUI_Dialog);
@@ -72,6 +77,43 @@
mDismissReceiver.unregister();
}
+ @Override
+ public void addListener(DialogListener listener) {
+ mDialogListeners.add(listener);
+ }
+
+ @Override
+ public void removeListener(DialogListener listener) {
+ mDialogListeners.remove(listener);
+ }
+
+ @Override
+ public void dismiss() {
+ super.dismiss();
+
+ for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
+ listener.onDismiss();
+ }
+ }
+
+ @Override
+ public void hide() {
+ super.hide();
+
+ for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
+ listener.onHide();
+ }
+ }
+
+ @Override
+ public void show() {
+ super.show();
+
+ for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
+ listener.onShow();
+ }
+ }
+
public void setShowForAllUsers(boolean show) {
setShowForAllUsers(this, show);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
new file mode 100644
index 0000000..6a49a6d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
@@ -0,0 +1,36 @@
+package com.android.systemui.statusbar.phone
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.animation.HostDialogProvider
+
+/** An implementation of [HostDialogProvider] to be used when animating SysUI dialogs. */
+class SystemUIHostDialogProvider : HostDialogProvider {
+ override fun createHostDialog(
+ context: Context,
+ theme: Int,
+ onCreateCallback: () -> Unit,
+ dismissOverride: (() -> Unit) -> Unit
+ ): Dialog {
+ return SystemUIHostDialog(context, theme, onCreateCallback, dismissOverride)
+ }
+
+ private class SystemUIHostDialog(
+ context: Context,
+ theme: Int,
+ private val onCreateCallback: () -> Unit,
+ private val dismissOverride: (() -> Unit) -> Unit
+ ) : SystemUIDialog(context, theme) {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ onCreateCallback()
+ }
+
+ override fun dismiss() {
+ dismissOverride {
+ super.dismiss()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 13eb75a..0d43b93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -28,6 +28,8 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -235,7 +237,9 @@
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
Optional<StartingSurface> startingSurfaceOptional,
TunerService tunerService,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ ActivityLaunchAnimator activityLaunchAnimator,
+ DialogLaunchAnimator dialogLaunchAnimator) {
return new StatusBar(
context,
notificationsController,
@@ -333,6 +337,8 @@
unlockedScreenOffAnimationController,
startingSurfaceOptional,
tunerService,
- dumpManager);
+ dumpManager,
+ activityLaunchAnimator,
+ dialogLaunchAnimator);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 62ba56a..eeff010 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -103,7 +103,16 @@
}
}
+ // Fix for b/199600334
+ override fun onEntryCleanUp(entry: NotificationEntry) {
+ removeChipIfNeeded(entry)
+ }
+
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ removeChipIfNeeded(entry)
+ }
+
+ private fun removeChipIfNeeded(entry: NotificationEntry) {
if (entry.sbn.key == callNotificationInfo?.key) {
removeChip()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index a0edc7c..1e52511 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -149,7 +149,7 @@
private void reinflate() {
int index = mStatusBarWindow.indexOfChild(mBrightnessMirror);
mStatusBarWindow.removeView(mBrightnessMirror);
- mBrightnessMirror = (FrameLayout) LayoutInflater.from(mBrightnessMirror.getContext())
+ mBrightnessMirror = (FrameLayout) LayoutInflater.from(mStatusBarWindow.getContext())
.inflate(R.layout.brightness_mirror_container, mStatusBarWindow, false);
mToggleSliderController = setMirrorLayout();
mStatusBarWindow.addView(mBrightnessMirror, index);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 3490e15..a543c7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -91,8 +91,7 @@
private int mImsType = IMS_TYPE_WWAN;
// Save entire info for logging, we only use the id.
final SubscriptionInfo mSubscriptionInfo;
- // @VisibleForDemoMode
- Map<String, MobileIconGroup> mNetworkToIconLookup;
+ private Map<String, MobileIconGroup> mNetworkToIconLookup;
// Since some pieces of the phone state are interdependent we store it locally,
// this could potentially become part of MobileState for simplification/complication
@@ -108,8 +107,6 @@
private Config mConfig;
@VisibleForTesting
boolean mInflateSignalStrengths = false;
- private MobileStatusTracker.Callback mCallback;
- private RegistrationCallback mRegistrationCallback;
private int mLastWwanLevel;
private int mLastWlanLevel;
private int mLastWlanCrossSimLevel;
@@ -121,6 +118,82 @@
// Where to copy the next state into.
private int mMobileStatusHistoryIndex;
+ private final MobileStatusTracker.Callback mMobileCallback =
+ new MobileStatusTracker.Callback() {
+ private String mLastStatus;
+
+ @Override
+ public void onMobileStatusChanged(boolean updateTelephony,
+ MobileStatus mobileStatus) {
+ if (Log.isLoggable(mTag, Log.DEBUG)) {
+ Log.d(mTag, "onMobileStatusChanged="
+ + " updateTelephony=" + updateTelephony
+ + " mobileStatus=" + mobileStatus.toString());
+ }
+ String currentStatus = mobileStatus.toString();
+ if (!currentStatus.equals(mLastStatus)) {
+ mLastStatus = currentStatus;
+ String status = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append(currentStatus)
+ .toString();
+ recordLastMobileStatus(status);
+ }
+ updateMobileStatus(mobileStatus);
+ if (updateTelephony) {
+ updateTelephony();
+ } else {
+ notifyListenersIfNecessary();
+ }
+ }
+ };
+
+ private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
+ @Override
+ public void onRegistered(ImsRegistrationAttributes attributes) {
+ Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
+ int imsTransportType = attributes.getTransportType();
+ int registrationAttributes = attributes.getAttributeFlags();
+ if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ mImsType = IMS_TYPE_WWAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+ getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+ if (registrationAttributes == 0) {
+ mImsType = IMS_TYPE_WLAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+ getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ } else if (registrationAttributes
+ == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
+ mImsType = IMS_TYPE_WLAN_CROSS_SIM;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+ getCallStrengthDescription(
+ mLastWlanCrossSimLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+ }
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo info) {
+ Log.d(mTag, "onDeregistered: " + "info=" + info);
+ mImsType = IMS_TYPE_WWAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+ getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+ };
+
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
public MobileSignalController(
@@ -144,8 +217,8 @@
mPhone = phone;
mDefaults = defaults;
mSubscriptionInfo = info;
- mNetworkNameSeparator = getTextIfExists(R.string.status_bar_network_name_separator)
- .toString();
+ mNetworkNameSeparator = getTextIfExists(
+ R.string.status_bar_network_name_separator).toString();
mNetworkNameDefault = getTextIfExists(
com.android.internal.R.string.lockscreen_carrier_default).toString();
mReceiverHandler = new Handler(receiverLooper);
@@ -165,83 +238,9 @@
updateTelephony();
}
};
- mCallback = new MobileStatusTracker.Callback() {
- private String mLastStatus;
-
- @Override
- public void onMobileStatusChanged(boolean updateTelephony,
- MobileStatus mobileStatus) {
- if (Log.isLoggable(mTag, Log.DEBUG)) {
- Log.d(mTag, "onMobileStatusChanged="
- + " updateTelephony=" + updateTelephony
- + " mobileStatus=" + mobileStatus.toString());
- }
- String currentStatus = mobileStatus.toString();
- if (!currentStatus.equals(mLastStatus)) {
- mLastStatus = currentStatus;
- String status = new StringBuilder()
- .append(SSDF.format(System.currentTimeMillis())).append(",")
- .append(currentStatus)
- .toString();
- recordLastMobileStatus(status);
- }
- updateMobileStatus(mobileStatus);
- if (updateTelephony) {
- updateTelephony();
- } else {
- notifyListenersIfNecessary();
- }
- }
- };
-
- mRegistrationCallback = new RegistrationCallback() {
- @Override
- public void onRegistered(ImsRegistrationAttributes attributes) {
- Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
- int imsTransportType = attributes.getTransportType();
- int registrationAttributes = attributes.getAttributeFlags();
- if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- if (registrationAttributes == 0) {
- mImsType = IMS_TYPE_WLAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
- getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (registrationAttributes
- == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
- mImsType = IMS_TYPE_WLAN_CROSS_SIM;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
- getCallStrengthDescription(
- mLastWlanCrossSimLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- }
- }
-
- @Override
- public void onUnregistered(ImsReasonInfo info) {
- Log.d(mTag, "onDeregistered: " + "info=" + info);
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- };
mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
- info, mDefaults, mCallback);
+ info, mDefaults, mMobileCallback);
mProviderModelBehavior = featureFlags.isCombinedStatusBarSignalIconsEnabled();
mProviderModelSetting = featureFlags.isProviderModelSettingEnabled();
}
@@ -385,89 +384,83 @@
if (mCurrentState.inetCondition == 0) {
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
}
- final boolean dataDisabled = (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
- || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA))
- && mCurrentState.userSetup;
+
+ final QsInfo qsInfo = getQsInfo(contentDescription, icons.dataType);
+ final SbInfo sbInfo = getSbInfo(contentDescription, icons.dataType);
+
+ MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
+ sbInfo.icon,
+ qsInfo.icon,
+ sbInfo.ratTypeIcon,
+ qsInfo.ratTypeIcon,
+ mCurrentState.hasActivityIn(),
+ mCurrentState.hasActivityOut(),
+ dataContentDescription,
+ dataContentDescriptionHtml,
+ qsInfo.description,
+ mSubscriptionInfo.getSubscriptionId(),
+ mCurrentState.roaming,
+ sbInfo.showTriangle);
+ callback.setMobileDataIndicators(mobileDataIndicators);
+ }
+
+ private QsInfo getQsInfo(String contentDescription, int dataTypeIcon) {
+ int qsTypeIcon = 0;
+ IconState qsIcon = null;
+ CharSequence qsDescription = null;
+
+ boolean pm = mProviderModelSetting || mProviderModelBehavior;
+ if (mCurrentState.dataSim) {
+ // If using provider model behavior, only show QS icons if the state is also default
+ if (pm && !mCurrentState.isDefault) {
+ return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
+ }
+
+ if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) {
+ qsTypeIcon = dataTypeIcon;
+ }
+
+ boolean qsIconVisible = mCurrentState.enabled && !mCurrentState.isEmergency;
+ qsIcon = new IconState(qsIconVisible, getQsCurrentIconId(), contentDescription);
+
+ if (!mCurrentState.isEmergency) {
+ qsDescription = mCurrentState.networkName;
+ }
+ }
+
+ return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
+ }
+
+ private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
+ final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
+ boolean showTriangle = false;
+ int typeIcon = 0;
+ IconState statusIcon = null;
if (mProviderModelBehavior) {
- // Show icon in QS when we are connected or data is disabled.
- boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
-
- int qsTypeIcon = 0;
- IconState qsIcon = null;
- CharSequence description = null;
- // Only send data sim callbacks to QS.
- if (mCurrentState.dataSim && mCurrentState.isDefault) {
- qsTypeIcon =
- (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
- qsIcon = new IconState(mCurrentState.enabled
- && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
- description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
- }
- boolean activityIn = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityIn;
- boolean activityOut = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityOut;
- showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
- boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
- int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
- showDataIcon |= mCurrentState.roaming;
- IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode,
+ boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
+ && (mCurrentState.dataSim && mCurrentState.isDefault);
+ typeIcon =
+ (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+ showDataIconStatusBar |= mCurrentState.roaming;
+ statusIcon = new IconState(
+ showDataIconStatusBar && !mCurrentState.airplaneMode,
getCurrentIconId(), contentDescription);
- MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
- statusIcon, qsIcon, typeIcon, qsTypeIcon,
- activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
- description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
- mCurrentState.roaming, showTriangle);
- callback.setMobileDataIndicators(mobileDataIndicators);
+
+ showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
} else {
- boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
- IconState statusIcon = new IconState(
+ statusIcon = new IconState(
mCurrentState.enabled && !mCurrentState.airplaneMode,
getCurrentIconId(), contentDescription);
- int qsTypeIcon = 0;
- IconState qsIcon = null;
- CharSequence description = null;
- // Only send data sim callbacks to QS.
- if (mProviderModelSetting) {
- if (mCurrentState.dataSim && mCurrentState.isDefault) {
- qsTypeIcon =
- (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
- qsIcon = new IconState(
- mCurrentState.enabled && !mCurrentState.isEmergency,
- getQsCurrentIconId(), contentDescription);
- description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
- }
- } else {
- if (mCurrentState.dataSim) {
- qsTypeIcon =
- (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
- qsIcon = new IconState(
- mCurrentState.enabled && !mCurrentState.isEmergency,
- getQsCurrentIconId(), contentDescription);
- description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
- }
- }
-
- boolean activityIn = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityIn;
- boolean activityOut = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityOut;
- showDataIcon &= mCurrentState.isDefault || dataDisabled;
- int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
- boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
- MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
- statusIcon, qsIcon, typeIcon, qsTypeIcon,
- activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
- description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
- mCurrentState.roaming, showTriangle);
- callback.setMobileDataIndicators(mobileDataIndicators);
+ boolean showDataIconInStatusBar =
+ (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+ typeIcon =
+ (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+ showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
}
+
+ return new SbInfo(showTriangle, typeIcon, statusIcon);
}
@Override
@@ -841,12 +834,15 @@
public void dump(PrintWriter pw) {
super.dump(pw);
pw.println(" mSubscription=" + mSubscriptionInfo + ",");
+ pw.println(" mProviderModelSetting=" + mProviderModelSetting + ",");
+ pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ",");
pw.println(" mServiceState=" + mServiceState + ",");
pw.println(" mSignalStrength=" + mSignalStrength + ",");
pw.println(" mTelephonyDisplayInfo=" + mTelephonyDisplayInfo + ",");
pw.println(" mDataState=" + mDataState + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
+ pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
pw.println(" MobileStatusHistory");
int size = 0;
for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -862,4 +858,31 @@
+ mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]);
}
}
+
+ /** Box for QS icon info */
+ private static final class QsInfo {
+ final int ratTypeIcon;
+ final IconState icon;
+ final CharSequence description;
+
+ QsInfo(int typeIcon, IconState iconState, CharSequence desc) {
+ ratTypeIcon = typeIcon;
+ icon = iconState;
+ description = desc;
+ }
+ }
+
+ /** Box for StatusBar icon info */
+ private static final class SbInfo {
+ final boolean showTriangle;
+ final int ratTypeIcon;
+ final IconState icon;
+
+ SbInfo(boolean show, int typeIcon, IconState iconState) {
+ showTriangle = show;
+ ratTypeIcon = typeIcon;
+ icon = iconState;
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index eeea699..9aec903 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -81,7 +81,7 @@
.append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString())
.append(",activityIn=").append(activityIn)
.append(",activityOut=").append(activityOut)
- .append(",description=").append(description)
+ .append(",qsDescription=").append(description)
.append(",isTransient=").append(isTransient)
.append(",statusLabel=").append(statusLabel)
.append(']').toString();
@@ -100,8 +100,7 @@
public boolean activityOut;
public CharSequence typeContentDescription;
public CharSequence typeContentDescriptionHtml;
- public CharSequence description;
- public boolean isWide;
+ public CharSequence qsDescription;
public int subId;
public boolean roaming;
public boolean showTriangle;
@@ -109,7 +108,7 @@
public MobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut,
CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml,
- CharSequence description, boolean isWide, int subId, boolean roaming,
+ CharSequence qsDescription, int subId, boolean roaming,
boolean showTriangle) {
this.statusIcon = statusIcon;
this.qsIcon = qsIcon;
@@ -119,8 +118,7 @@
this.activityOut = activityOut;
this.typeContentDescription = typeContentDescription;
this.typeContentDescriptionHtml = typeContentDescriptionHtml;
- this.description = description;
- this.isWide = isWide;
+ this.qsDescription = qsDescription;
this.subId = subId;
this.roaming = roaming;
this.showTriangle = showTriangle;
@@ -137,8 +135,7 @@
.append(",activityOut=").append(activityOut)
.append(",typeContentDescription=").append(typeContentDescription)
.append(",typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
- .append(",description=").append(description)
- .append(",isWide=").append(isWide)
+ .append(",description=").append(qsDescription)
.append(",subId=").append(subId)
.append(",roaming=").append(roaming)
.append(",showTriangle=").append(showTriangle)
@@ -186,11 +183,13 @@
default void setCallIndicator(IconState statusIcon, int subId) {}
}
- public interface EmergencyListener {
+ /** */
+ interface EmergencyListener {
void setEmergencyCallsOnly(boolean emergencyOnly);
}
- public static class IconState {
+ /** */
+ class IconState {
public final boolean visible;
public final int icon;
public final String contentDescription;
@@ -220,7 +219,7 @@
* Tracks changes in access points. Allows listening for changes, scanning for new APs,
* and connecting to new ones.
*/
- public interface AccessPointController {
+ interface AccessPointController {
void addAccessPointCallback(AccessPointCallback callback);
void removeAccessPointCallback(AccessPointCallback callback);
void scanForAccessPoints();
@@ -230,7 +229,7 @@
boolean canConfigWifi();
boolean canConfigMobileData();
- public interface AccessPointCallback {
+ interface AccessPointCallback {
void onAccessPointsChanged(List<WifiEntry> accessPoints);
void onSettingsActivityTriggered(Intent settingsIntent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index fc19564..3f7ddc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -163,7 +163,7 @@
int qsTypeIcon = 0;
IconState qsIcon = null;
if (sbVisible) {
- qsTypeIcon = icons.qsDataType;
+ qsTypeIcon = icons.dataType;
qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(),
contentDescription);
}
@@ -172,7 +172,7 @@
MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
statusIcon, qsIcon, typeIcon, qsTypeIcon,
mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
- dataContentDescriptionHtml, description, icons.isWide,
+ dataContentDescriptionHtml, description,
mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true
);
callback.setMobileDataIndicators(mobileDataIndicators);
diff --git a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
index a22793b..516efc0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
@@ -109,6 +109,12 @@
}
}
+ @Override
+ public void setTintList(ColorStateList tint) {
+ super.setTintList(tint);
+ mTint = tint;
+ }
+
private void applyTint() {
if (getDrawable() != null && mTint != null) {
getDrawable().mutate().setTintList(mTint);
diff --git a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
index de5a363..14190fa 100644
--- a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
@@ -23,8 +23,9 @@
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
-import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import javax.inject.Inject;
@@ -34,39 +35,54 @@
*/
@SysUISingleton
public class CarrierConfigTracker extends BroadcastReceiver {
- private final SparseArray<Boolean> mCallStrengthConfigs = new SparseArray<>();
- private final SparseArray<Boolean> mNoCallingConfigs = new SparseArray<>();
+ private final SparseBooleanArray mCallStrengthConfigs = new SparseBooleanArray();
+ private final SparseBooleanArray mNoCallingConfigs = new SparseBooleanArray();
+ private final SparseBooleanArray mCarrierProvisionsWifiMergedNetworks =
+ new SparseBooleanArray();
private final CarrierConfigManager mCarrierConfigManager;
private boolean mDefaultCallStrengthConfigLoaded;
private boolean mDefaultCallStrengthConfig;
private boolean mDefaultNoCallingConfigLoaded;
private boolean mDefaultNoCallingConfig;
+ private boolean mDefaultCarrierProvisionsWifiMergedNetworksLoaded;
+ private boolean mDefaultCarrierProvisionsWifiMergedNetworks;
@Inject
- public CarrierConfigTracker(Context context) {
+ public CarrierConfigTracker(Context context, BroadcastDispatcher broadcastDispatcher) {
mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
- context.registerReceiver(
+ broadcastDispatcher.registerReceiver(
this, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
}
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction() == CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) {
- int subId = intent.getIntExtra(
- CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- return;
- }
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(subId);
- if (b != null) {
- boolean hideNoCallingConfig = b.getBoolean(
- CarrierConfigManager.KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL);
- boolean displayCallStrengthIcon = b.getBoolean(
- CarrierConfigManager.KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL);
- mCallStrengthConfigs.put(subId, displayCallStrengthIcon);
- mNoCallingConfigs.put(subId, hideNoCallingConfig);
- }
+ if (!CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+ return;
+ }
+
+ final int subId = intent.getIntExtra(
+ CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return;
+ }
+
+ final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+ if (config == null) {
+ return;
+ }
+
+ synchronized (mCallStrengthConfigs) {
+ mCallStrengthConfigs.put(subId, config.getBoolean(
+ CarrierConfigManager.KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL));
+ }
+ synchronized (mNoCallingConfigs) {
+ mNoCallingConfigs.put(subId, config.getBoolean(
+ CarrierConfigManager.KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL));
+ }
+ synchronized (mCarrierProvisionsWifiMergedNetworks) {
+ mCarrierProvisionsWifiMergedNetworks.put(subId, config.getBoolean(
+ CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL));
}
}
@@ -74,8 +90,10 @@
* Returns the KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL value for the given subId.
*/
public boolean getCallStrengthConfig(int subId) {
- if (mCallStrengthConfigs.indexOfKey(subId) >= 0) {
- return mCallStrengthConfigs.get(subId);
+ synchronized (mCallStrengthConfigs) {
+ if (mCallStrengthConfigs.indexOfKey(subId) >= 0) {
+ return mCallStrengthConfigs.get(subId);
+ }
}
if (!mDefaultCallStrengthConfigLoaded) {
mDefaultCallStrengthConfig =
@@ -90,8 +108,10 @@
* Returns the KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL value for the given subId.
*/
public boolean getNoCallingConfig(int subId) {
- if (mNoCallingConfigs.indexOfKey(subId) >= 0) {
- return mNoCallingConfigs.get(subId);
+ synchronized (mNoCallingConfigs) {
+ if (mNoCallingConfigs.indexOfKey(subId) >= 0) {
+ return mNoCallingConfigs.get(subId);
+ }
}
if (!mDefaultNoCallingConfigLoaded) {
mDefaultNoCallingConfig =
@@ -101,4 +121,22 @@
}
return mDefaultNoCallingConfig;
}
+
+ /**
+ * Returns the KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL value for the given subId.
+ */
+ public boolean getCarrierProvisionsWifiMergedNetworksBool(int subId) {
+ synchronized (mCarrierProvisionsWifiMergedNetworks) {
+ if (mCarrierProvisionsWifiMergedNetworks.indexOfKey(subId) >= 0) {
+ return mCarrierProvisionsWifiMergedNetworks.get(subId);
+ }
+ }
+ if (!mDefaultCarrierProvisionsWifiMergedNetworksLoaded) {
+ mDefaultCarrierProvisionsWifiMergedNetworks =
+ CarrierConfigManager.getDefaultConfig().getBoolean(
+ CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL);
+ mDefaultCarrierProvisionsWifiMergedNetworksLoaded = true;
+ }
+ return mDefaultCarrierProvisionsWifiMergedNetworks;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
index 3b64f9f..1059d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -69,6 +69,10 @@
return super.getChangingConfigurations() or ActivityInfo.CONFIG_DENSITY
}
+ override fun canApplyTheme(): Boolean {
+ return (drawable?.canApplyTheme() ?: false) || super.canApplyTheme()
+ }
+
private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() {
override fun newDrawable(): Drawable {
return newDrawable(null, null)
@@ -82,5 +86,9 @@
override fun getChangingConfigurations(): Int {
return wrappedState.changingConfigurations
}
+
+ override fun canApplyTheme(): Boolean {
+ return true
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
index db2aca8..a981045 100644
--- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
@@ -40,8 +40,8 @@
this.wallpaperInfo = wallpaperInfo
}
- private val shouldUseDefaultUnfoldTransition: Boolean
- get() = wallpaperInfo?.shouldUseDefaultUnfoldTransition()
+ private val shouldUseDefaultDeviceStateChangeTransition: Boolean
+ get() = wallpaperInfo?.shouldUseDefaultDeviceStateChangeTransition()
?: true
fun setNotificationShadeZoom(zoomOut: Float) {
@@ -50,7 +50,7 @@
}
fun setUnfoldTransitionZoom(zoomOut: Float) {
- if (shouldUseDefaultUnfoldTransition) {
+ if (shouldUseDefaultDeviceStateChangeTransition) {
unfoldTransitionZoomOut = zoomOut
updateZoom()
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index d44fb76..9917777 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -116,7 +116,7 @@
if (toolbar != null) {
setActionBar(toolbar);
}
- setTitle("");
+ getActionBar().setDisplayShowTitleEnabled(false);
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeAsUpIndicator(getHomeIndicatorDrawable());
getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index cc35a8f..d819fa2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -46,6 +46,7 @@
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
+ private val launchAnimator = LaunchAnimator(mContext, isForTesting = true)
@Mock lateinit var callback: ActivityLaunchAnimator.Callback
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
@@ -56,7 +57,8 @@
@Before
fun setup() {
- activityLaunchAnimator = ActivityLaunchAnimator(callback, mContext)
+ activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator)
+ activityLaunchAnimator.callback = callback
}
private fun startIntentWithAnimation(
@@ -120,7 +122,8 @@
@Test
fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() {
`when`(callback.isOnKeyguard()).thenReturn(true)
- val animator = ActivityLaunchAnimator(callback, context)
+ val animator = ActivityLaunchAnimator(launchAnimator)
+ animator.callback = callback
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
var animationAdapter: RemoteAnimationAdapter? = null
@@ -208,7 +211,7 @@
private class TestLaunchAnimatorController(
override var launchContainer: ViewGroup
) : ActivityLaunchAnimator.Controller {
- override fun createAnimatorState() = ActivityLaunchAnimator.State(
+ override fun createAnimatorState() = LaunchAnimator.State(
top = 100,
bottom = 200,
left = 300,
@@ -232,7 +235,7 @@
}
override fun onLaunchAnimationProgress(
- state: ActivityLaunchAnimator.State,
+ state: LaunchAnimator.State,
progress: Float,
linearProgress: Float
) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
new file mode 100644
index 0000000..5bcf828
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -0,0 +1,186 @@
+package com.android.systemui.animation
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.ViewUtils
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.LinearLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class DialogLaunchAnimatorTest : SysuiTestCase() {
+ private val launchAnimator = LaunchAnimator(context, isForTesting = true)
+ private val hostDialogprovider = TestHostDialogProvider()
+ private val dialogLaunchAnimator =
+ DialogLaunchAnimator(context, launchAnimator, hostDialogprovider)
+
+ @Test
+ fun testShowDialogFromView() {
+ // Show the dialog. showFromView() must be called on the main thread with a dialog created
+ // on the main thread too.
+ val (dialog, hostDialog) = runOnMainThreadAndWaitForIdleSync {
+ val touchSurfaceRoot = LinearLayout(context)
+ val touchSurface = View(context)
+ touchSurfaceRoot.addView(touchSurface)
+
+ // We need to attach the root to the window manager otherwise the exit animation will
+ // be skipped
+ ViewUtils.attachView(touchSurfaceRoot)
+
+ val dialog = TestDialog(context)
+ val hostDialog =
+ dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog
+ dialog to hostDialog
+ }
+
+ // Only the host dialog is actually showing.
+ assertTrue(hostDialog.isShowing)
+ assertFalse(dialog.isShowing)
+
+ // The dialog onStart() method was called but not onStop().
+ assertTrue(dialog.onStartCalled)
+ assertFalse(dialog.onStopCalled)
+
+ // The dialog content has been stolen and is shown inside the host dialog.
+ val hostDialogContent = hostDialog.findViewById<ViewGroup>(android.R.id.content)
+ assertEquals(0, dialog.findViewById<ViewGroup>(android.R.id.content).childCount)
+ assertEquals(1, hostDialogContent.childCount)
+
+ val hostDialogRoot = hostDialogContent.getChildAt(0) as ViewGroup
+ assertEquals(1, hostDialogRoot.childCount)
+ assertEquals(dialog.contentView, hostDialogRoot.getChildAt(0))
+
+ // If we are dozing, the host dialog window also fades out.
+ runOnMainThreadAndWaitForIdleSync { dialogLaunchAnimator.onDozeAmountChanged(0.5f) }
+ assertTrue(hostDialog.window!!.decorView.alpha < 1f)
+
+ // Hiding/showing/dismissing the dialog should hide/show/dismiss the host dialog given that
+ // it's a ListenableDialog.
+ runOnMainThreadAndWaitForIdleSync { dialog.hide() }
+ assertFalse(hostDialog.isShowing)
+ assertFalse(dialog.isShowing)
+
+ runOnMainThreadAndWaitForIdleSync { dialog.show() }
+ assertTrue(hostDialog.isShowing)
+ assertFalse(dialog.isShowing)
+
+ assertFalse(dialog.onStopCalled)
+ runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
+ assertFalse(hostDialog.isShowing)
+ assertFalse(dialog.isShowing)
+ assertTrue(hostDialog.wasDismissed)
+ assertTrue(dialog.onStopCalled)
+ }
+
+ private fun <T : Any> runOnMainThreadAndWaitForIdleSync(f: () -> T): T {
+ lateinit var result: T
+ context.mainExecutor.execute {
+ result = f()
+ }
+ waitForIdleSync()
+ return result
+ }
+
+ private class TestHostDialogProvider : HostDialogProvider {
+ override fun createHostDialog(
+ context: Context,
+ theme: Int,
+ onCreateCallback: () -> Unit,
+ dismissOverride: (() -> Unit) -> Unit
+ ): Dialog = TestHostDialog(context, onCreateCallback, dismissOverride)
+ }
+
+ private class TestHostDialog(
+ context: Context,
+ private val onCreateCallback: () -> Unit,
+ private val dismissOverride: (() -> Unit) -> Unit
+ ) : Dialog(context) {
+ var wasDismissed = false
+
+ init {
+ // We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
+ window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ onCreateCallback()
+ }
+
+ override fun dismiss() {
+ dismissOverride {
+ super.dismiss()
+ wasDismissed = true
+ }
+ }
+ }
+
+ private class TestDialog(context: Context) : Dialog(context), ListenableDialog {
+ private val listeners = hashSetOf<DialogListener>()
+ val contentView = View(context)
+ var onStartCalled = false
+ var onStopCalled = false
+
+ init {
+ // We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
+ window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(contentView)
+ }
+
+ override fun onStart() {
+ super.onStart()
+ onStartCalled = true
+ }
+
+ override fun onStop() {
+ super.onStart()
+ onStopCalled = true
+ }
+
+ override fun addListener(listener: DialogListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeListener(listener: DialogListener) {
+ listeners.remove(listener)
+ }
+
+ override fun dismiss() {
+ super.dismiss()
+ notifyListeners { onDismiss() }
+ }
+
+ override fun hide() {
+ super.hide()
+ notifyListeners { onHide() }
+ }
+
+ override fun show() {
+ super.show()
+ notifyListeners { onShow() }
+ }
+
+ private fun notifyListeners(notify: DialogListener.() -> Unit) {
+ for (listener in HashSet(listeners)) {
+ listener.notify()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
index 8cba25d..58e0cb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
@@ -32,7 +32,7 @@
fun animatingOrphanViewDoesNotCrash() {
val ghostedView = LinearLayout(mContext)
val controller = GhostedViewLaunchAnimatorController(ghostedView)
- val state = ActivityLaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
+ val state = LaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
controller.onIntentStarted(willAnimate = true)
controller.onLaunchAnimationStart(isExpandingFullyAbove = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index af6dee7..c464cad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -32,6 +32,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.DisplayMetrics;
+import android.util.Pair;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
@@ -128,20 +129,7 @@
@Test
public void testUpdateFingerprintLocationOnInit() {
// GIVEN fp sensor location is available pre-attached
- final PointF udfpsLocation = new PointF(50, 75);
- final int radius = 33;
- final FingerprintSensorPropertiesInternal fpProps =
- new FingerprintSensorPropertiesInternal(
- /* sensorId */ 0,
- /* strength */ 0,
- /* max enrollments per user */ 5,
- /* component info */ new ArrayList<>(),
- /* sensorType */ 3,
- /* resetLockoutRequiresHwToken */ false,
- List.of(new SensorLocationInternal("" /* displayId */,
- (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
- when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
- when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
+ Pair<Integer, PointF> udfps = setupUdfps();
// WHEN lock icon view controller is initialized and attached
mLockIconViewController.init();
@@ -149,8 +137,8 @@
mAttachListener.onViewAttachedToWindow(mLockIconView);
// THEN lock icon view location is updated with the same coordinates as fpProps
- verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius));
- assertEquals(udfpsLocation, mPointCaptor.getValue());
+ verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first));
+ assertEquals(udfps.second, mPointCaptor.getValue());
}
@Test
@@ -164,6 +152,47 @@
// GIVEN fp sensor location is available post-atttached
captureAuthControllerCallback();
+ Pair<Integer, PointF> udfps = setupUdfps();
+
+ // WHEN all authenticators are registered
+ mAuthControllerCallback.onAllAuthenticatorsRegistered();
+
+ // THEN lock icon view location is updated with the same coordinates as fpProps
+ verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first));
+ assertEquals(udfps.second, mPointCaptor.getValue());
+ }
+
+ @Test
+ public void testLockIconViewBackgroundEnabledWhenUdfpsIsAvailable() {
+ // GIVEN Udpfs sensor location is available
+ setupUdfps();
+
+ mLockIconViewController.init();
+ captureAttachListener();
+
+ // WHEN the view is attached
+ mAttachListener.onViewAttachedToWindow(mLockIconView);
+
+ // THEN the lock icon view background should be enabled
+ verify(mLockIconView).setUseBackground(true);
+ }
+
+ @Test
+ public void testLockIconViewBackgroundDisabledWhenUdfpsIsUnavailable() {
+ // GIVEN Udfps sensor location is not available
+ when(mAuthController.getUdfpsSensorLocation()).thenReturn(null);
+
+ mLockIconViewController.init();
+ captureAttachListener();
+
+ // WHEN the view is attached
+ mAttachListener.onViewAttachedToWindow(mLockIconView);
+
+ // THEN the lock icon view background should be disabled
+ verify(mLockIconView).setUseBackground(false);
+ }
+
+ private Pair<Integer, PointF> setupUdfps() {
final PointF udfpsLocation = new PointF(50, 75);
final int radius = 33;
final FingerprintSensorPropertiesInternal fpProps =
@@ -179,12 +208,7 @@
when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
- // WHEN all authenticators are registered
- mAuthControllerCallback.onAllAuthenticatorsRegistered();
-
- // THEN lock icon view location is updated with the same coordinates as fpProps
- verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius));
- assertEquals(udfpsLocation, mPointCaptor.getValue());
+ return new Pair(radius, udfpsLocation);
}
private void captureAuthControllerCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index d879186..28aed20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -86,7 +86,7 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener,
+ mediaDataFilter = MediaDataFilter(context, broadcastDispatcher, mediaResumeListener,
lockscreenUserManager, executor, clock)
mediaDataFilter.mediaDataManager = mediaDataManager
mediaDataFilter.addListener(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 5c3108c..d864aae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -4,6 +4,7 @@
import android.app.PendingIntent
import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceTarget
+import android.content.Intent
import android.graphics.Bitmap
import android.media.MediaDescription
import android.media.MediaMetadata
@@ -51,6 +52,7 @@
private const val SESSION_ARTIST = "artist"
private const val SESSION_TITLE = "title"
private const val USER_ID = 0
+private val DISMISS_INTENT = Intent().apply { action = "dismiss" }
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
@@ -83,6 +85,7 @@
lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
@Mock private lateinit var mediaRecommendationItem: SmartspaceAction
+ @Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction
lateinit var mediaDataManager: MediaDataManager
lateinit var mediaNotification: StatusBarNotification
@Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
@@ -146,8 +149,12 @@
// treat mediaSessionBasedFilter as a listener for testing.
listener = mediaSessionBasedFilter
- val recommendationExtras = Bundle()
- recommendationExtras.putString("package_name", PACKAGE_NAME)
+ val recommendationExtras = Bundle().apply {
+ putString("package_name", PACKAGE_NAME)
+ putParcelable("dismiss_intent", DISMISS_INTENT)
+ }
+ whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras)
+ whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction)
whenever(mediaRecommendationItem.extras).thenReturn(recommendationExtras)
whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
@@ -380,7 +387,8 @@
verify(listener).onSmartspaceMediaDataLoaded(
eq(KEY_MEDIA_SMARTSPACE),
eq(SmartspaceMediaData(KEY_MEDIA_SMARTSPACE, true /* isActive */, true /*isValid */,
- PACKAGE_NAME, null, listOf(mediaRecommendationItem), 0)),
+ PACKAGE_NAME, mediaSmartspaceBaseAction, listOf(mediaRecommendationItem),
+ DISMISS_INTENT, 0)),
eq(false))
}
@@ -391,7 +399,28 @@
verify(listener).onSmartspaceMediaDataLoaded(
eq(KEY_MEDIA_SMARTSPACE),
eq(EMPTY_SMARTSPACE_MEDIA_DATA
- .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true, isValid = false)),
+ .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
+ isValid = false, dismissIntent = DISMISS_INTENT)),
+ eq(false))
+ }
+
+ @Test
+ fun testOnSmartspaceMediaDataLoaded_hasNullIntent_callsListener() {
+ val recommendationExtras = Bundle().apply {
+ putString("package_name", PACKAGE_NAME)
+ putParcelable("dismiss_intent", null)
+ }
+ whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras)
+ whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction)
+ whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf())
+
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+
+ verify(listener).onSmartspaceMediaDataLoaded(
+ eq(KEY_MEDIA_SMARTSPACE),
+ eq(EMPTY_SMARTSPACE_MEDIA_DATA
+ .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
+ isValid = false, dismissIntent = null)),
eq(false))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 2c68661..25ca8c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -54,6 +54,7 @@
// Mock
private MediaOutputController mMediaOutputController = mock(MediaOutputController.class);
+ private MediaOutputDialog mMediaOutputDialog = mock(MediaOutputDialog.class);
private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
private Icon mIcon = mock(Icon.class);
@@ -65,7 +66,7 @@
@Before
public void setUp() {
- mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
+ mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController, mMediaOutputDialog);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 9bd07b8..053851e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -39,6 +39,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -63,6 +64,7 @@
private NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
private MediaOutputController mMediaOutputController;
@@ -75,7 +77,7 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index d1a617b..f7e60ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -49,6 +49,7 @@
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -91,6 +92,7 @@
private NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private Context mSpyContext;
private MediaOutputController mMediaOutputController;
@@ -113,7 +115,7 @@
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -157,7 +159,7 @@
public void start_withoutPackageName_verifyMediaControllerInit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.start(mCb);
@@ -178,7 +180,7 @@
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.start(mCb);
@@ -449,7 +451,7 @@
public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 86f6bde..8a3ea56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -36,6 +36,7 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -65,6 +66,7 @@
private final NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
@@ -74,10 +76,11 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false,
mMediaOutputController, mUiEventLogger);
+ mMediaOutputDialog.show();
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
when(mMediaDevice.getFeatures()).thenReturn(mFeatures);
@@ -123,6 +126,7 @@
public void onCreate_ShouldLogVisibility() {
MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false,
mMediaOutputController, mUiEventLogger);
+ testDialog.show();
testDialog.dismissDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index c296ff5..e8cd6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -34,6 +34,7 @@
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -64,6 +65,7 @@
private NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private MediaOutputGroupDialog mMediaOutputGroupDialog;
private MediaOutputController mMediaOutputController;
@@ -73,10 +75,11 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
mMediaOutputController);
+ mMediaOutputGroupDialog.show();
when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 36228dc..e37f422 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -168,9 +168,12 @@
Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
when(windowManager.getDefaultDisplay()).thenReturn(
defaultDisplay);
- WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
+ WindowMetrics maximumWindowMetrics = mContext.getSystemService(WindowManager.class)
.getMaximumWindowMetrics();
- when(windowManager.getMaximumWindowMetrics()).thenReturn(metrics);
+ when(windowManager.getMaximumWindowMetrics()).thenReturn(maximumWindowMetrics);
+ WindowMetrics currentWindowMetrics = mContext.getSystemService(WindowManager.class)
+ .getCurrentWindowMetrics();
+ when(windowManager.getCurrentWindowMetrics()).thenReturn(currentWindowMetrics);
doNothing().when(windowManager).addView(any(), any());
mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
mSysuiTestableContextExternal.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 6ff5aa0..2e1fb07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -95,11 +96,11 @@
Resources mResources;
@Mock
Configuration mConfiguration;
+ @Mock
+ Runnable mHorizontalLayoutListener;
private QSPanelControllerBase<QSPanel> mController;
-
-
/** Implementation needed to ensure we have a reflectively-available class name. */
private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
@@ -239,17 +240,43 @@
when(mMediaHost.getVisible()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
+ when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape");
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+ mController.init();
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait");
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+ mController.init();
assertThat(mController.shouldUseHorizontalLayout()).isFalse();
}
+
+ @Test
+ public void testChangeConfiguration_shouldUseHorizontalLayout() {
+ when(mMediaHost.getVisible()).thenReturn(true);
+ mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener);
+
+ // When device is rotated to landscape
+ mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+
+ // Then the layout changes
+ assertThat(mController.shouldUseHorizontalLayout()).isTrue();
+ verify(mHorizontalLayoutListener).run(); // not invoked
+
+ // When it is rotated back to portrait
+ mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
+ mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+
+ // Then the layout changes back
+ assertThat(mController.shouldUseHorizontalLayout()).isFalse();
+ verify(mHorizontalLayoutListener, times(2)).run();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index a21f488..06a4ae0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -23,6 +23,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -98,6 +100,10 @@
@Mock
CommandQueue mCommandQueue;
FalsingManagerFake mFalsingManager = new FalsingManagerFake();
+ @Mock
+ Resources mResources;
+ @Mock
+ Configuration mConfiguration;
private QSPanelController mController;
@@ -109,7 +115,8 @@
when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout);
when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
- when(mQSPanel.getResources()).thenReturn(mContext.getResources());
+ when(mQSPanel.getResources()).thenReturn(mResources);
+ when(mResources.getConfiguration()).thenReturn(mConfiguration);
when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
when(mToggleSliderViewControllerFactory.create(any(), any()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 126b332..a1b7210 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -232,7 +232,7 @@
MobileDataIndicators indicators = new MobileDataIndicators(
mock(NetworkController.IconState.class),
mock(NetworkController.IconState.class),
- 0, 0, true, true, "", "", "", true, 0, true, true);
+ 0, 0, true, true, "", "", "", 0, true, true);
mSignalCallback.setMobileDataIndicators(indicators);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
index 5e2d8fd..b4a66297 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -3,6 +3,7 @@
import android.app.AlarmManager
import android.app.PendingIntent
import android.os.Handler
+import android.provider.AlarmClock
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -98,6 +99,11 @@
}
@Test
+ fun testDefaultIntentShowAlarms() {
+ assertThat(tile.defaultIntent.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS)
+ }
+
+ @Test
fun testInactiveByDefault() {
assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index baddacc..f9d5be6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -1,11 +1,15 @@
package com.android.systemui.qs.tiles.dialog;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -14,8 +18,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.Animator;
import android.content.Context;
import android.content.Intent;
+import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
@@ -25,7 +31,11 @@
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.testing.TestableResources;
import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -41,14 +51,19 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.toast.SystemUIToast;
+import com.android.systemui.toast.ToastFactory;
+import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -65,6 +80,11 @@
private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
+ //SystemUIToast
+ private static final int GRAVITY_FLAGS = Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL;
+ private static final int TOAST_MESSAGE_STRING_ID = 1;
+ private static final String TOAST_MESSAGE_STRING = "toast message";
+
@Mock
private WifiManager mWifiManager;
@Mock
@@ -74,6 +94,8 @@
@Mock
private Handler mHandler;
@Mock
+ private Handler mWorkerHandler;
+ @Mock
private ActivityStarter mActivityStarter;
@Mock
private GlobalSettings mGlobalSettings;
@@ -92,6 +114,8 @@
@Mock
private WifiEntry mWifiEntry4;
@Mock
+ private MergedCarrierEntry mMergedCarrierEntry;
+ @Mock
private ServiceState mServiceState;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@@ -99,7 +123,20 @@
private WifiUtils.InternetIconInjector mWifiIconInjector;
@Mock
InternetDialogController.InternetDialogCallback mInternetDialogCallback;
+ @Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private ToastFactory mToastFactory;
+ @Mock
+ private SystemUIToast mSystemUIToast;
+ @Mock
+ private View mToastView;
+ @Mock
+ private Animator mAnimator;
+ @Mock
+ private CarrierConfigTracker mCarrierConfigTracker;
+ private TestableResources mTestableResources;
private MockInternetDialogController mInternetDialogController;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private List<WifiEntry> mAccessPoints = new ArrayList<>();
@@ -108,6 +145,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mTestableResources = mContext.getOrCreateTestableResources();
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
when(mConnectedEntry.isDefaultNetwork()).thenReturn(true);
@@ -118,13 +156,21 @@
when(mWifiEntry4.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
+ when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{SUB_ID});
+ when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
+ when(mToastFactory.createToast(any(), anyString(), anyString(), anyInt(), anyInt()))
+ .thenReturn(mSystemUIToast);
+ when(mSystemUIToast.getView()).thenReturn(mToastView);
+ when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS);
+ when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator);
mInternetDialogController = new MockInternetDialogController(mContext,
mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController,
mSubscriptionManager, mTelephonyManager, mWifiManager,
mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
- mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController);
+ mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController,
+ mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker);
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mInternetDialogController.mOnSubscriptionsChangedListener);
mInternetDialogController.onStart(mInternetDialogCallback, true);
@@ -134,6 +180,44 @@
}
@Test
+ public void connectCarrierNetwork_mergedCarrierEntryCanConnect_connectAndCreateSysUiToast() {
+ when(mMergedCarrierEntry.canConnect()).thenReturn(true);
+ mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
+ TOAST_MESSAGE_STRING);
+
+ mInternetDialogController.connectCarrierNetwork();
+
+ verify(mMergedCarrierEntry).connect(null /* callback */, false /* showToast */);
+ verify(mToastFactory).createToast(any(), eq(TOAST_MESSAGE_STRING), anyString(), anyInt(),
+ anyInt());
+ }
+
+ @Test
+ public void makeOverlayToast_withGravityFlags_addViewWithLayoutParams() {
+ mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING);
+
+ mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID);
+
+ ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor = ArgumentCaptor.forClass(
+ WindowManager.LayoutParams.class);
+ verify(mWindowManager).addView(eq(mToastView), paramsCaptor.capture());
+ WindowManager.LayoutParams params = paramsCaptor.getValue();
+ assertThat(params.format).isEqualTo(PixelFormat.TRANSLUCENT);
+ assertThat(params.type).isEqualTo(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+ assertThat(params.horizontalWeight).isEqualTo(TOAST_PARAMS_HORIZONTAL_WEIGHT);
+ assertThat(params.verticalWeight).isEqualTo(TOAST_PARAMS_VERTICAL_WEIGHT);
+ }
+
+ @Test
+ public void makeOverlayToast_withAnimation_verifyAnimatorStart() {
+ mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING);
+
+ mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID);
+
+ verify(mAnimator).start();
+ }
+
+ @Test
public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
mInternetDialogController.setAirplaneModeEnabled(true);
@@ -444,6 +528,80 @@
verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
}
+ @Test
+ public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(true);
+ mAccessPoints.clear();
+ mAccessPoints.add(mWifiEntry1);
+ mAccessPoints.add(mWifiEntry2);
+ mAccessPoints.add(mWifiEntry3);
+ mAccessPoints.add(mWifiEntry4);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ mWifiEntries.add(mWifiEntry2);
+ mWifiEntries.add(mWifiEntry3);
+ mWifiEntries.add(mWifiEntry4);
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+
+ // If the Ethernet exists, then Wi-Fi entries will cut last one.
+ reset(mInternetDialogCallback);
+ mInternetDialogController.mHasEthernet = true;
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.remove(mWifiEntry4);
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+
+ // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(false);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.remove(mWifiEntry3);
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+ }
+
+ @Test
+ public void setMergedCarrierWifiEnabledIfNeed_carrierProvisionsEnabled_doNothing() {
+ when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
+ .thenReturn(true);
+
+ mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+
+ verify(mMergedCarrierEntry, never()).setEnabled(anyBoolean());
+ }
+
+ @Test
+ public void setMergedCarrierWifiEnabledIfNeed_mergedCarrierEntryEmpty_doesntCrash() {
+ when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
+ .thenReturn(false);
+ when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null);
+
+ mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+ }
+
+ @Test
+ public void setMergedCarrierWifiEnabledIfNeed_neededSetMergedCarrierEntry_setTogether() {
+ when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
+ .thenReturn(false);
+
+ mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+
+ verify(mMergedCarrierEntry).setEnabled(true);
+
+ mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, false);
+
+ verify(mMergedCarrierEntry).setEnabled(false);
+ }
+
private String getResourcesString(String name) {
return mContext.getResources().getString(getResourcesId(name));
}
@@ -465,11 +623,14 @@
@Main Handler handler, @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher,
KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings,
- KeyguardStateController keyguardStateController) {
+ KeyguardStateController keyguardStateController, WindowManager windowManager,
+ ToastFactory toastFactory, Handler workerHandler,
+ CarrierConfigTracker carrierConfigTracker) {
super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
- keyguardStateController);
+ keyguardStateController, windowManager, toastFactory, workerHandler,
+ carrierConfigTracker);
mGlobalSettings = globalSettings;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index fa9c053..c42b64a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -67,6 +67,7 @@
private InternetDialog mInternetDialog;
private View mDialogView;
private View mSubTitle;
+ private LinearLayout mEthernet;
private LinearLayout mMobileDataToggle;
private LinearLayout mWifiToggle;
private LinearLayout mConnectedWifi;
@@ -97,6 +98,7 @@
mDialogView = mInternetDialog.mDialogView;
mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+ mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_network_layout);
mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
@@ -139,6 +141,46 @@
}
@Test
+ public void updateDialog_apmOffAndHasEthernet_showEthernet() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDialogController.hasEthernet()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_apmOffAndNoEthernet_hideEthernet() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDialogController.hasEthernet()).thenReturn(false);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_apmOnAndHasEthernet_showEthernet() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mInternetDialogController.hasEthernet()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoEthernet_hideEthernet() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mInternetDialogController.hasEthernet()).thenReturn(false);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void updateDialog_withApmOn_mobileDataLayoutGone() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 09a3d35..f5cab1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -23,6 +23,7 @@
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
@@ -725,6 +726,20 @@
verify(mIndicationAreaBottom).announceForAccessibility(eq(faceHelpMsg));
}
+ @Test
+ public void testEmptyOwnerInfoHidesIndicationArea() {
+ createController();
+
+ // GIVEN the owner info is set to an empty string
+ when(mLockPatternUtils.getDeviceOwnerInfo()).thenReturn("");
+
+ // WHEN asked to update the indication area
+ mController.setVisible(true);
+
+ // THEN the owner info should be hidden
+ verifyHideIndication(INDICATION_TYPE_OWNER_INFO);
+ }
+
private void sendUpdateDisclosureBroadcast() {
mBroadcastReceiver.onReceive(mContext, new Intent());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index c8fec02..fd93280 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -445,6 +445,20 @@
verify(smartspaceView2).registerDataProvider(plugin)
}
+ @Test
+ fun testConnectAttemptBeforeInitializationShouldNotCreateSession() {
+ // GIVEN an uninitalized smartspaceView
+ // WHEN the device is provisioned
+ `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
+ `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+ deviceProvisionedListener.onDeviceProvisionedChanged()
+
+ // THEN no calls to createSmartspaceSession should occur
+ verify(smartspaceManager, never()).createSmartspaceSession(any())
+ // THEN no listeners should be registered
+ verify(configurationController, never()).addCallback(any())
+ }
+
private fun connectSession() {
val view = controller.buildAndConnectView(fakeParent)
smartspaceView = view as SmartspaceView
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 1f3e1c7..902d115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -32,6 +32,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -81,6 +82,7 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -94,6 +96,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -120,6 +123,7 @@
@Mock private KeyguardEnvironment mEnvironment;
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationEntryListener mEntryListener;
+ @Mock private NotifCollectionListener mNotifCollectionListener;
@Mock private NotificationRemoveInterceptor mRemoveInterceptor;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private RankingMap mRankingMap;
@@ -215,6 +219,7 @@
mEnvironment));
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
+ mEntryManager.addCollectionListener(mNotifCollectionListener);
mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
setUserSentiment(mSbn.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
@@ -318,13 +323,20 @@
eq(mEntry), any(), eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
}
+ /** Regression test for b/201097913. */
@Test
- public void testRemoveNotification_whilePending() {
+ public void testRemoveNotification_whilePending_onlyCollectionListenerNotified() {
+ // Add and then remove a pending entry (entry that hasn't been inflated).
mEntryManager.addNotification(mSbn, mRankingMap);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
+ // Verify that only the listener for the NEW pipeline is notified.
+ // Old pipeline:
verify(mEntryListener, never()).onEntryRemoved(
- eq(mEntry), any(), eq(false /* removedByUser */), eq(UNDEFINED_DISMISS_REASON));
+ argThat(matchEntryOnSbn()), any(), anyBoolean(), anyInt());
+ // New pipeline:
+ verify(mNotifCollectionListener).onEntryRemoved(
+ argThat(matchEntryOnSbn()), anyInt());
}
@Test
@@ -639,6 +651,11 @@
PendingIntent.FLAG_IMMUTABLE)).build();
}
+ // TODO(b/201321631): Update more tests to use this function instead of eq(mEntry).
+ private ArgumentMatcher<NotificationEntry> matchEntryOnSbn() {
+ return e -> e.getSbn().equals(mSbn);
+ }
+
private static class FakeNotificationLifetimeExtender implements NotificationLifetimeExtender {
private NotificationSafeToRemoveCallback mCallback;
private boolean mExtendLifetimes = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 33928ea..f5fb98b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -778,6 +778,18 @@
}
@Test
+ public void testSwitchesToBigClockInSplitShadeOnAod() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+
+ mNotificationPanelViewController.setDozing(true, false, null);
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE);
+ }
+
+ @Test
public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 5115614..6e3d5ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -308,18 +308,4 @@
verify(mBouncer).updateKeyguardPosition(1.0f);
}
-
- @Test
- public void testNavBarHiddenWhenSleepAnimationStarts() {
- mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- assertTrue(mStatusBarKeyguardViewManager.isNavBarVisible());
-
- // Verify that the nav bar is hidden when the screen off animation starts
- doReturn(true).when(mUnlockedScreenOffAnimationController).isScreenOffAnimationPlaying();
- mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
- assertFalse(mStatusBarKeyguardViewManager.isNavBarVisible());
-
- mWakefulnessLifecycle.dispatchFinishedWakingUp();
- assertTrue(mStatusBarKeyguardViewManager.isNavBarVisible());
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 88a3827..fada64e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -77,6 +77,8 @@
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -274,6 +276,8 @@
@Mock private StartingSurface mStartingSurface;
@Mock private OperatorNameViewController mOperatorNameViewController;
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
+ @Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
+ @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -452,7 +456,9 @@
mUnlockedScreenOffAnimationController,
Optional.of(mStartingSurface),
mTunerService,
- mock(DumpManager.class));
+ mock(DumpManager.class),
+ mActivityLaunchAnimator,
+ mDialogLaunchAnimator);
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
any(ViewGroup.class), any(KeyguardBypassController.class)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 4476fd8..efe2c17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -221,6 +221,18 @@
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
+ /** Regression test for b/201097913. */
+ @Test
+ fun onEntryCleanUp_callNotifAddedThenRemoved_listenerNotified() {
+ val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+ notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
+ reset(mockOngoingCallListener)
+
+ notifCollectionListener.onEntryCleanUp(ongoingCallNotifEntry)
+
+ verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
+ }
+
/** Regression test for b/188491504. */
@Test
fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_listenerNotified() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 2418243..3c55df9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -124,7 +124,7 @@
boolean roaming = true;
MobileDataIndicators indicators = new MobileDataIndicators(
status, qs, type, qsType, in, out, typeDescription,
- typeDescriptionHtml, description, wide, subId, roaming, true);
+ typeDescriptionHtml, description, subId, roaming, true);
mHandler.setMobileDataIndicators(indicators);
waitForCallbacks();
@@ -141,8 +141,7 @@
assertEquals(out, expected.activityOut);
assertEquals(typeDescription, expected.typeContentDescription);
assertEquals(typeDescriptionHtml, expected.typeContentDescriptionHtml);
- assertEquals(description, expected.description);
- assertEquals(wide, expected.isWide);
+ assertEquals(description, expected.qsDescription);
assertEquals(subId, expected.subId);
assertTrue(expected.roaming);
assertTrue(expected.showTriangle);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index 3cb19e3..de86821 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -91,7 +91,7 @@
@Test
fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() {
wallaperController.onWallpaperInfoUpdated(createWallpaperInfo(
- useDefaultUnfoldTransition = false
+ useDefaultTransition = false
))
wallaperController.setUnfoldTransitionZoom(0.5f)
@@ -136,9 +136,10 @@
verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat())
}
- private fun createWallpaperInfo(useDefaultUnfoldTransition: Boolean = true): WallpaperInfo {
+ private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo {
val info = mock(WallpaperInfo::class.java)
- whenever(info.shouldUseDefaultUnfoldTransition()).thenReturn(useDefaultUnfoldTransition)
+ whenever(info.shouldUseDefaultDeviceStateChangeTransition())
+ .thenReturn(useDefaultTransition)
return info
}
}
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index d356894..ef1d581 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -8,6 +8,7 @@
android:directBootAware="true">
<service android:name=".CameraExtensionsProxyService"
+ android:visibleToInstantApps="true"
android:exported="true">
</service>
<uses-library android:name="androidx.camera.extensions.impl" android:required="false" />
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index fdba098..48f5b51 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -476,7 +476,11 @@
*/
public static void addTombstoneToDropBox(Context ctx, File tombstone, boolean proto) {
final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
- final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+ if (db == null) {
+ Slog.e(TAG, "Can't log tombstone: DropBoxManager not available");
+ return;
+ }
+
HashMap<String, Long> timestamps = readTimestamps();
try {
if (proto) {
@@ -484,7 +488,7 @@
} else {
final String headers = getBootHeadersToLogAndUpdate();
addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
- TAG_TOMBSTONE);
+ TAG_TOMBSTONE);
}
} catch (IOException e) {
Slog.e(TAG, "Can't log tombstone", e);
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 483250a..68fd0c1 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -92,6 +92,8 @@
27533 notification_autogrouped (key|3)
# notification was removed from an autogroup
275534 notification_unautogrouped (key|3)
+# when a notification is adjusted via assistant
+27535 notification_adjusted (key|3),(adjustment_type|3),(new_value|3)
# ---------------------------
# Watchdog.java
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index c3543e7..c1c9fbb 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -327,18 +327,23 @@
}
}
- private static int getMaxRescueLevel() {
- return SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)
- ? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET;
+ private static int getMaxRescueLevel(boolean mayPerformFactoryReset) {
+ if (!mayPerformFactoryReset
+ || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
+ return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
+ }
+ return LEVEL_FACTORY_RESET;
}
/**
* Get the rescue level to perform if this is the n-th attempt at mitigating failure.
*
* @param mitigationCount: the mitigation attempt number (1 = first attempt etc.)
+ * @param mayPerformFactoryReset: whether or not a factory reset may be performed for the given
+ * failure.
* @return the rescue level for the n-th mitigation attempt.
*/
- private static int getRescueLevel(int mitigationCount) {
+ private static int getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset) {
if (mitigationCount == 1) {
return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
} else if (mitigationCount == 2) {
@@ -346,9 +351,9 @@
} else if (mitigationCount == 3) {
return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
} else if (mitigationCount == 4) {
- return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT);
+ return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT);
} else if (mitigationCount >= 5) {
- return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET);
+ return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET);
} else {
Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
return LEVEL_NONE;
@@ -614,7 +619,8 @@
@FailureReasons int failureReason, int mitigationCount) {
if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
- return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
+ return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
+ mayPerformFactoryReset(failedPackage)));
} else {
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
@@ -628,7 +634,8 @@
}
if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
- final int level = getRescueLevel(mitigationCount);
+ final int level = getRescueLevel(mitigationCount,
+ mayPerformFactoryReset(failedPackage));
executeRescueLevel(mContext,
failedPackage == null ? null : failedPackage.getPackageName(), level);
return true;
@@ -653,12 +660,7 @@
} catch (PackageManager.NameNotFoundException ignore) {
}
- try {
- ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
- return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
+ return isPersistentSystemApp(packageName);
}
@Override
@@ -666,7 +668,7 @@
if (isDisabled()) {
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
- return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
+ return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true));
}
@Override
@@ -674,7 +676,8 @@
if (isDisabled()) {
return false;
}
- executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount));
+ executeRescueLevel(mContext, /*failedPackage=*/ null,
+ getRescueLevel(mitigationCount, true));
return true;
}
@@ -683,6 +686,29 @@
return NAME;
}
+ /**
+ * Returns {@code true} if the failing package is non-null and performing a reboot or
+ * prompting a factory reset is an acceptable mitigation strategy for the package's
+ * failure, {@code false} otherwise.
+ */
+ private boolean mayPerformFactoryReset(@Nullable VersionedPackage failingPackage) {
+ if (failingPackage == null) {
+ return false;
+ }
+
+ return isPersistentSystemApp(failingPackage.getPackageName());
+ }
+
+ private boolean isPersistentSystemApp(@NonNull String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+ return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
@NonNull String namespace) {
// Record it in calling packages to namespace map
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 91b2440..1176c50 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -420,9 +420,12 @@
}
synchronized (mLock) {
- if (mSuppressReminders.containsKey(new Pair<>(sensor, user))) {
+ UserHandle parentUser = UserHandle.of(mUserManagerInternal
+ .getProfileParentId(user.getIdentifier()));
+ if (mSuppressReminders.containsKey(new Pair<>(sensor, parentUser))) {
Log.d(TAG,
- "Suppressed sensor privacy reminder for " + packageName + "/" + user);
+ "Suppressed sensor privacy reminder for " + packageName + "/"
+ + parentUser);
return;
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 69c2926..8727932 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -221,6 +221,9 @@
@GuardedBy("mLock")
private final Set<Integer> mFuseMountedUser = new ArraySet<>();
+ @GuardedBy("mLock")
+ private final Set<Integer> mCeStoragePreparedUsers = new ArraySet<>();
+
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -4864,5 +4867,19 @@
}
return primaryVolumeIds;
}
+
+ @Override
+ public void markCeStoragePrepared(int userId) {
+ synchronized (mLock) {
+ mCeStoragePreparedUsers.add(userId);
+ }
+ }
+
+ @Override
+ public boolean isCeStoragePrepared(int userId) {
+ synchronized (mLock) {
+ return mCeStoragePreparedUsers.contains(userId);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b4413a4..63301ac 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1025,7 +1025,6 @@
return;
}
- int phoneId = getPhoneIdFromSubId(subId);
synchronized (mRecords) {
// register
IBinder b = callback.asBinder();
@@ -1048,21 +1047,24 @@
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
// force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ if (DBG) {
+ log("invalid subscription id, use default id");
+ }
r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
} else {//APP specify subID
r.subId = subId;
}
- r.phoneId = phoneId;
+ r.phoneId = getPhoneIdFromSubId(r.subId);
r.eventList = events;
if (DBG) {
- log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
+ log("listen: Register r=" + r + " r.subId=" + r.subId + " r.phoneId=" + r.phoneId);
}
- if (notifyNow && validatePhoneId(phoneId)) {
+ if (notifyNow && validatePhoneId(r.phoneId)) {
if (events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)){
try {
- if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
- ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+ if (VDBG) log("listen: call onSSC state=" + mServiceState[r.phoneId]);
+ ServiceState rawSs = new ServiceState(mServiceState[r.phoneId]);
if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onServiceStateChanged(rawSs);
} else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
@@ -1078,8 +1080,8 @@
}
if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) {
try {
- if (mSignalStrength[phoneId] != null) {
- int gsmSignalStrength = mSignalStrength[phoneId]
+ if (mSignalStrength[r.phoneId] != null) {
+ int gsmSignalStrength = mSignalStrength[r.phoneId]
.getGsmSignalStrength();
r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
: gsmSignalStrength));
@@ -1092,7 +1094,7 @@
TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
try {
r.callback.onMessageWaitingIndicatorChanged(
- mMessageWaiting[phoneId]);
+ mMessageWaiting[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1101,7 +1103,7 @@
TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
try {
r.callback.onCallForwardingIndicatorChanged(
- mCallForwarding[phoneId]);
+ mCallForwarding[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1109,11 +1111,11 @@
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) {
try {
- if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
+ if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[r.phoneId]);
if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
// null will be translated to empty CellLocation object in client.
- r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
+ r.callback.onCellLocationChanged(mCellIdentity[r.phoneId]);
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1121,38 +1123,38 @@
}
if (events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)) {
try {
- r.callback.onLegacyCallStateChanged(mCallState[phoneId],
- getCallIncomingNumber(r, phoneId));
+ r.callback.onLegacyCallStateChanged(mCallState[r.phoneId],
+ getCallIncomingNumber(r, r.phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
try {
- r.callback.onCallStateChanged(mCallState[phoneId]);
+ r.callback.onCallStateChanged(mCallState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
try {
- r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
- mDataConnectionNetworkType[phoneId]);
+ r.callback.onDataConnectionStateChanged(mDataConnectionState[r.phoneId],
+ mDataConnectionNetworkType[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)) {
try {
- r.callback.onDataActivity(mDataActivity[phoneId]);
+ r.callback.onDataActivity(mDataActivity[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
try {
- if (mSignalStrength[phoneId] != null) {
- r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+ if (mSignalStrength[r.phoneId] != null) {
+ r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1162,8 +1164,8 @@
TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
updateReportSignalStrengthDecision(r.subId);
try {
- if (mSignalStrength[phoneId] != null) {
- r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+ if (mSignalStrength[r.phoneId] != null) {
+ r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1172,11 +1174,13 @@
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
try {
- if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
- + mCellInfo.get(phoneId));
+ if (DBG_LOC) {
+ log("listen: mCellInfo[" + r.phoneId + "] = "
+ + mCellInfo.get(r.phoneId));
+ }
if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ r.callback.onCellInfoChanged(mCellInfo.get(r.phoneId));
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1184,22 +1188,22 @@
}
if (events.contains(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)) {
try {
- r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
+ r.callback.onPreciseCallStateChanged(mPreciseCallState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
try {
- r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
- mCallPreciseDisconnectCause[phoneId]);
+ r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[r.phoneId],
+ mCallPreciseDisconnectCause[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
try {
- r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
+ r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(r.phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1208,7 +1212,7 @@
TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
try {
for (PreciseDataConnectionState pdcs
- : mPreciseDataConnectionStates.get(phoneId).values()) {
+ : mPreciseDataConnectionStates.get(r.phoneId).values()) {
r.callback.onPreciseDataConnectionStateChanged(pdcs);
}
} catch (RemoteException ex) {
@@ -1225,29 +1229,29 @@
if (events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
try {
r.callback.onVoiceActivationStateChanged(
- mVoiceActivationState[phoneId]);
+ mVoiceActivationState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
try {
- r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
+ r.callback.onDataActivationStateChanged(mDataActivationState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
try {
- r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+ r.callback.onUserMobileDataStateChanged(mUserMobileDataState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) {
try {
- if (mTelephonyDisplayInfos[phoneId] != null) {
- r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
+ if (mTelephonyDisplayInfos[r.phoneId] != null) {
+ r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[r.phoneId]);
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1284,20 +1288,20 @@
}
if (events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)) {
try {
- r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
+ r.callback.onSrvccStateChanged(mSrvccState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) {
try {
- r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
+ r.callback.onCallAttributesChanged(mCallAttributes[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED)) {
- BarringInfo barringInfo = mBarringInfo.get(phoneId);
+ BarringInfo barringInfo = mBarringInfo.get(r.phoneId);
BarringInfo biNoLocation = barringInfo != null
? barringInfo.createLocationInfoSanitizedCopy() : null;
if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
@@ -1315,8 +1319,8 @@
r.callback.onPhysicalChannelConfigChanged(
shouldSanitizeLocationForPhysicalChannelConfig(r)
? getLocationSanitizedConfigs(
- mPhysicalChannelConfigs.get(phoneId))
- : mPhysicalChannelConfigs.get(phoneId));
+ mPhysicalChannelConfigs.get(r.phoneId))
+ : mPhysicalChannelConfigs.get(r.phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1325,7 +1329,7 @@
TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)) {
try {
r.callback.onDataEnabledChanged(
- mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]);
+ mIsDataEnabled[r.phoneId], mDataEnabledReason[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1333,9 +1337,9 @@
if (events.contains(
TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)) {
try {
- if (mLinkCapacityEstimateLists.get(phoneId) != null) {
+ if (mLinkCapacityEstimateLists.get(r.phoneId) != null) {
r.callback.onLinkCapacityEstimateChanged(mLinkCapacityEstimateLists
- .get(phoneId));
+ .get(r.phoneId));
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1577,7 +1581,7 @@
}
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
ServiceState stateToSend;
@@ -1639,7 +1643,7 @@
if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
&& r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
if (DBG) {
log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
@@ -1650,7 +1654,7 @@
if ((activationType == SIM_ACTIVATION_TYPE_DATA)
&& r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
if (DBG) {
log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
@@ -1692,7 +1696,7 @@
TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
|| r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG) {
log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r
@@ -1706,7 +1710,7 @@
}
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
@@ -1753,7 +1757,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCarrierNetworkChange(active);
} catch (RemoteException ex) {
@@ -1785,7 +1789,7 @@
for (Record r : mRecords) {
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)
- && idMatch(r.subId, subId, phoneId)
+ && idMatch(r, subId, phoneId)
&& (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
@@ -1819,7 +1823,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onMessageWaitingIndicatorChanged(mwi);
} catch (RemoteException ex) {
@@ -1846,7 +1850,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onUserMobileDataStateChanged(state);
} catch (RemoteException ex) {
@@ -1885,7 +1889,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
- && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (!mConfigurationProvider.isDisplayInfoNrAdvancedSupported(
r.callingPackage, Binder.getCallingUserHandle())) {
@@ -1937,7 +1941,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCallForwardingIndicatorChanged(cfi);
} catch (RemoteException ex) {
@@ -1966,7 +1970,7 @@
// Notify by correct subId.
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onDataActivity(state);
} catch (RemoteException ex) {
@@ -2014,7 +2018,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG) {
log("Notify data connection state changed on sub: " + subId);
@@ -2039,7 +2043,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onPreciseDataConnectionStateChanged(preciseState);
} catch (RemoteException ex) {
@@ -2086,7 +2090,7 @@
for (Record r : mRecords) {
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)
- && idMatch(r.subId, subId, phoneId)
+ && idMatch(r, subId, phoneId)
&& (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
@@ -2140,7 +2144,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
} catch (RemoteException ex) {
@@ -2149,7 +2153,7 @@
}
if (notifyCallAttributes && r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
} catch (RemoteException ex) {
@@ -2174,7 +2178,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
mCallPreciseDisconnectCause[phoneId]);
@@ -2199,7 +2203,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifyImsCallDisconnectCause: mImsReasonInfo="
@@ -2231,7 +2235,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifySrvccStateChanged: mSrvccState=" + state + " r=" + r);
@@ -2260,7 +2264,7 @@
}
if ((r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_OEM_HOOK_RAW))
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onOemHookRawEvent(rawData);
} catch (RemoteException ex) {
@@ -2340,7 +2344,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onRadioPowerStateChanged(state);
} catch (RemoteException ex) {
@@ -2369,7 +2373,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
if (VDBG) {
@@ -2456,7 +2460,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
} catch (RemoteException ex) {
@@ -2487,7 +2491,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_REGISTRATION_FAILURE)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onRegistrationFailed(
checkFineLocationAccess(r, Build.VERSION_CODES.BASE)
@@ -2530,7 +2534,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_BARRING_INFO_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifyBarringInfo: mBarringInfo="
@@ -2575,7 +2579,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifyPhysicalChannelConfig: mPhysicalChannelConfigs="
@@ -2642,7 +2646,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onDataEnabledChanged(enabled, reason);
} catch (RemoteException ex) {
@@ -2677,7 +2681,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (VDBG) {
log("notifyAllowedNetworkTypesChanged: reason= " + reason
@@ -2719,7 +2723,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onLinkCapacityEstimateChanged(linkCapacityEstimateList);
} catch (RemoteException ex) {
@@ -3169,33 +3173,24 @@
}
/**
- * If the registrant specified a subId, then we should only notify it if subIds match.
- * If the registrant registered with DEFAULT subId, we should notify only when the related subId
- * is default subId (which could be INVALID if there's no default subId).
+ * Match the sub id or phone id of the event to the record
*
- * This should be the correct way to check record ID match. in idMatch the record's phoneId is
- * speculated based on subId passed by the registrant so it's not a good reference.
- * But to avoid triggering potential regression only replace idMatch with it when an issue with
- * idMatch is reported. Eventually this should replace all instances of idMatch.
+ * We follow the rules below:
+ * 1) If sub id of the event is invalid, phone id should be used.
+ * 2) The event on default sub should be notified to the records
+ * which register the default sub id.
+ * 3) Sub id should be exactly matched for all other cases.
*/
- private boolean idMatchWithoutDefaultPhoneCheck(int subIdInRecord, int subIdToNotify) {
- if (subIdInRecord == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- return (subIdToNotify == mDefaultSubId);
- } else {
- return (subIdInRecord == subIdToNotify);
- }
- }
+ boolean idMatch(Record r, int subId, int phoneId) {
- boolean idMatch(int rSubId, int subId, int phoneId) {
-
- if(subId < 0) {
- // Invalid case, we need compare phoneId with default one.
- return (mDefaultPhoneId == phoneId);
+ if (subId < 0) {
+ // Invalid case, we need compare phoneId.
+ return (r.phoneId == phoneId);
}
- if(rSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ if (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
return (subId == mDefaultSubId);
} else {
- return (rSubId == subId);
+ return (r.subId == subId);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 85ffaa2..18ed958 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1550,6 +1550,13 @@
private static final int INDEX_TOTAL_MEMTRACK_GL = 14;
private static final int INDEX_LAST = 15;
+ /**
+ * Used to notify activity lifecycle events.
+ */
+ @Nullable
+ volatile ActivityManagerInternal.VoiceInteractionManagerProvider
+ mVoiceInteractionManagerProvider;
+
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -1886,6 +1893,14 @@
return mAppOpsService;
}
+ /**
+ * Sets the internal voice interaction manager service.
+ */
+ private void setVoiceInteractionManagerProvider(
+ @Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
+ mVoiceInteractionManagerProvider = provider;
+ }
+
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
@@ -2737,6 +2752,11 @@
|| event == Event.ACTIVITY_DESTROYED)) {
contentCaptureService.notifyActivityEvent(userId, activity, event);
}
+ // TODO(b/201234353): Move the logic to client side.
+ if (mVoiceInteractionManagerProvider != null && (event == Event.ACTIVITY_PAUSED
+ || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED)) {
+ mVoiceInteractionManagerProvider.notifyActivityEventChanged();
+ }
}
/**
@@ -7736,6 +7756,17 @@
} else {
killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
"Too many Binders sent to SYSTEM");
+ // We need to run a GC here, because killing the processes involved
+ // actually isn't guaranteed to free up the proxies; in fact, if the
+ // GC doesn't run for a long time, we may even exceed the global
+ // proxy limit for a process (20000), resulting in system_server itself
+ // being killed.
+ // Note that the GC here might not actually clean up all the proxies,
+ // because the binder reference decrements will come in asynchronously;
+ // but if new processes belonging to the UID keep adding proxies, we
+ // will get another callback here, and run the GC again - this time
+ // cleaning up the old proxies.
+ VMRuntime.getRuntime().requestConcurrentGC();
}
}, mHandler);
t.traceEnd(); // setBinderProxies
@@ -15404,12 +15435,14 @@
}
@Override
- public void updateDeviceIdleTempAllowlist(int[] appids, int changingUid, boolean adding,
- long durationMs, @TempAllowListType int type, @ReasonCode int reasonCode,
- @Nullable String reason, int callingUid) {
+ public void updateDeviceIdleTempAllowlist(@Nullable int[] appids, int changingUid,
+ boolean adding, long durationMs, @TempAllowListType int type,
+ @ReasonCode int reasonCode, @Nullable String reason, int callingUid) {
synchronized (ActivityManagerService.this) {
synchronized (mProcLock) {
- mDeviceIdleTempAllowlist = appids;
+ if (appids != null) {
+ mDeviceIdleTempAllowlist = appids;
+ }
if (adding) {
if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
// Note, the device idle temp-allowlist are by app-ids, but here
@@ -15419,12 +15452,7 @@
callingUid));
}
} else {
- // Note in the removing case, we need to remove all the UIDs matching
- // the appId, because DeviceIdle's temp-allowlist are based on AppIds,
- // not UIDs.
- // For eacmple, "cmd deviceidle tempallowlist -r PACKAGE" will
- // not only remove this app for user 0, but for all users.
- mFgsStartTempAllowList.removeAppId(UserHandle.getAppId(changingUid));
+ mFgsStartTempAllowList.removeUid(changingUid);
}
setAppIdTempAllowlistStateLSP(changingUid, adding);
}
@@ -16358,6 +16386,12 @@
return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code,
intent, resolvedType, finishedReceiver, requiredPermission, options);
}
+
+ @Override
+ public void setVoiceInteractionManagerProvider(
+ @Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
+ ActivityManagerService.this.setVoiceInteractionManagerProvider(provider);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 50278fd..e6ffcfc 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import android.os.Process;
+import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.Slog;
@@ -38,21 +40,40 @@
private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
- @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+ @VisibleForTesting
+ static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+ @VisibleForTesting
+ static final String KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS =
+ "oom_re_ranking_preserve_top_n_apps";
+ @VisibleForTesting
+ static final int DEFAULT_PRESERVE_TOP_N_APPS = 3;
+ @VisibleForTesting
+ static final String KEY_OOM_RE_RANKING_USE_FREQUENT_RSS = "oom_re_ranking_rss_use_frequent_rss";
+ @VisibleForTesting
+ static final boolean DEFAULT_USE_FREQUENT_RSS = true;
+ @VisibleForTesting
+ static final String KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS = "oom_re_ranking_rss_update_rate_ms";
+ @VisibleForTesting
+ static final long DEFAULT_RSS_UPDATE_RATE_MS = 10_000; // 10 seconds
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
new ScoreComparator();
private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR =
new CacheUseComparator();
+ private static final Comparator<RankedProcessRecord> RSS_COMPARATOR =
+ new RssComparator();
private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR =
new LastRssComparator();
private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR =
@@ -61,20 +82,33 @@
private final Object mPhenotypeFlagLock = new Object();
private final ActivityManagerService mService;
+ private final ProcessDependencies mProcessDependencies;
private final ActivityManagerGlobalLock mProcLock;
private final Object mProfilerLock;
@GuardedBy("mPhenotypeFlagLock")
private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting
+ int mPreserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting
+ boolean mUseFrequentRss = DEFAULT_USE_FREQUENT_RSS;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting
+ long mRssUpdateRateMs = DEFAULT_RSS_UPDATE_RATE_MS;
// Weight to apply to the LRU ordering.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
+ @VisibleForTesting
+ float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
// Weight to apply to the ordering by number of times the process has been added to the cache.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
+ @VisibleForTesting
+ float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
// Weight to apply to the ordering by RSS used by the processes.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
+ @VisibleForTesting
+ float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
// Positions to replace in the lru list.
@GuardedBy("mPhenotypeFlagLock")
@@ -93,6 +127,12 @@
updateUseOomReranking();
} else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
updateNumberToReRank();
+ } else if (KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS.equals(name)) {
+ updatePreserveTopNApps();
+ } else if (KEY_OOM_RE_RANKING_USE_FREQUENT_RSS.equals(name)) {
+ updateUseFrequentRss();
+ } else if (KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS.equals(name)) {
+ updateRssUpdateRateMs();
} else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
updateLruWeight();
} else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
@@ -106,9 +146,15 @@
};
CacheOomRanker(final ActivityManagerService service) {
+ this(service, new ProcessDependenciesImpl());
+ }
+
+ @VisibleForTesting
+ CacheOomRanker(final ActivityManagerService service, ProcessDependencies processDependencies) {
mService = service;
mProcLock = service.mProcLock;
mProfilerLock = service.mAppProfiler.mProfilerLock;
+ mProcessDependencies = processDependencies;
}
/** Load settings from device config and register a listener for changes. */
@@ -160,6 +206,31 @@
}
@GuardedBy("mPhenotypeFlagLock")
+ private void updatePreserveTopNApps() {
+ int preserveTopNApps = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, DEFAULT_PRESERVE_TOP_N_APPS);
+ if (preserveTopNApps < 0) {
+ Slog.w(OomAdjuster.TAG,
+ "Found negative value for preserveTopNApps, setting to default: "
+ + preserveTopNApps);
+ preserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
+ }
+ mPreserveTopNApps = preserveTopNApps;
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
+ private void updateRssUpdateRateMs() {
+ mRssUpdateRateMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS, DEFAULT_RSS_UPDATE_RATE_MS);
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
+ private void updateUseFrequentRss() {
+ mUseFrequentRss = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_OOM_RE_RANKING_USE_FREQUENT_RSS, DEFAULT_USE_FREQUENT_RSS);
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
private void updateLruWeight() {
mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT);
@@ -183,9 +254,39 @@
*/
@GuardedBy({"mService", "mProcLock"})
void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
+ // The lruList is a list of processes ordered by how recently they were used. The
+ // least-recently-used apps are at the beginning of the list. We keep track of two
+ // indices in the lruList:
+ //
+ // getNumberToReRank=5, preserveTopNApps=3, lruProcessServiceStart=7,
+ // lruList=
+ // 0: app A ^
+ // 1: app B | These apps are re-ranked, as they are the first five apps (see
+ // 2: app C | getNumberToReRank), excluding...
+ // 3: app D v
+ // 4: app E ^
+ // 5: app F | The three most-recently-used apps in the cache (see preserveTopNApps).
+ // 6: app G v
+ // 7: service A ^
+ // 8: service B | Everything beyond lruProcessServiceStart is ignored, as these aren't
+ // 9: service C | apps.
+ // 10: activity A |
+ // ... |
+ //
+ // `numProcessesEvaluated` moves across the apps (indices 0-6) or until we've found enough
+ // apps to re-rank, and made sure none of them are in the top `preserveTopNApps` apps.
+ // Re-ranked apps are copied into `scoredProcessRecords`, where the re-ranking calculation
+ // happens.
+ //
+ // Note that some apps in the `lruList` can be skipped, if they don't pass
+ //`appCanBeReRanked`.
+
float lruWeight;
float usesWeight;
float rssWeight;
+ int preserveTopNApps;
+ boolean useFrequentRss;
+ long rssUpdateRateMs;
int[] lruPositions;
RankedProcessRecord[] scoredProcessRecords;
@@ -193,6 +294,9 @@
lruWeight = mLruWeight;
usesWeight = mUsesWeight;
rssWeight = mRssWeight;
+ preserveTopNApps = mPreserveTopNApps;
+ useFrequentRss = mUseFrequentRss;
+ rssUpdateRateMs = mRssUpdateRateMs;
lruPositions = mLruPositions;
scoredProcessRecords = mScoredProcessRecords;
}
@@ -202,52 +306,98 @@
return;
}
+ int numProcessesEvaluated = 0;
// Collect the least recently used processes to re-rank, only rank cached
// processes further down the list than mLruProcessServiceStart.
- int cachedProcessPos = 0;
- for (int i = 0; i < lruProcessServiceStart
- && cachedProcessPos < scoredProcessRecords.length; ++i) {
- ProcessRecord app = lruList.get(i);
+ int numProcessesReRanked = 0;
+ while (numProcessesEvaluated < lruProcessServiceStart
+ && numProcessesReRanked < scoredProcessRecords.length) {
+ ProcessRecord process = lruList.get(numProcessesEvaluated);
// Processes that will be assigned a cached oom adj score.
- if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
- >= ProcessList.UNKNOWN_ADJ) {
- scoredProcessRecords[cachedProcessPos].proc = app;
- scoredProcessRecords[cachedProcessPos].score = 0.0f;
- lruPositions[cachedProcessPos] = i;
- ++cachedProcessPos;
+ if (appCanBeReRanked(process)) {
+ scoredProcessRecords[numProcessesReRanked].proc = process;
+ scoredProcessRecords[numProcessesReRanked].score = 0.0f;
+ lruPositions[numProcessesReRanked] = numProcessesEvaluated;
+ ++numProcessesReRanked;
+ }
+ ++numProcessesEvaluated;
+ }
+
+ // Count how many apps we're not re-ranking (up to preserveTopNApps).
+ int numProcessesNotReRanked = 0;
+ while (numProcessesEvaluated < lruProcessServiceStart
+ && numProcessesNotReRanked < preserveTopNApps) {
+ ProcessRecord process = lruList.get(numProcessesEvaluated);
+ if (appCanBeReRanked(process)) {
+ numProcessesNotReRanked++;
+ }
+ numProcessesEvaluated++;
+ }
+ // Exclude the top `preserveTopNApps` apps from re-ranking.
+ if (numProcessesNotReRanked < preserveTopNApps) {
+ numProcessesReRanked -= preserveTopNApps - numProcessesNotReRanked;
+ if (numProcessesReRanked < 0) {
+ numProcessesReRanked = 0;
}
}
- // TODO maybe ensure a certain number above this in the cache before re-ranking.
- if (cachedProcessPos < scoredProcessRecords.length) {
- // Ignore we don't have enough processes to worry about re-ranking.
- return;
+ if (useFrequentRss) {
+ // Update RSS values for re-ranked apps.
+ long nowMs = SystemClock.elapsedRealtime();
+ for (int i = 0; i < numProcessesReRanked; ++i) {
+ RankedProcessRecord scoredProcessRecord = scoredProcessRecords[i];
+ long sinceUpdateMs =
+ nowMs - scoredProcessRecord.proc.mState.getCacheOomRankerRssTimeMs();
+ if (scoredProcessRecord.proc.mState.getCacheOomRankerRss() != 0
+ && sinceUpdateMs < rssUpdateRateMs) {
+ continue;
+ }
+
+ long[] rss = mProcessDependencies.getRss(scoredProcessRecord.proc.getPid());
+ if (rss == null || rss.length == 0) {
+ Slog.e(
+ OomAdjuster.TAG,
+ "Process.getRss returned bad value, not re-ranking: "
+ + Arrays.toString(rss));
+ return;
+ }
+ // First element is total RSS:
+ // frameworks/base/core/jni/android_util_Process.cpp:1192
+ scoredProcessRecord.proc.mState.setCacheOomRankerRss(rss[0], nowMs);
+ scoredProcessRecord.proc.mProfile.setLastRss(rss[0]);
+ }
}
// Add scores for each of the weighted features we want to rank based on.
if (lruWeight > 0.0f) {
// This doesn't use the LRU list ordering as after the first re-ranking
// that will no longer be lru.
- Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+ LAST_ACTIVITY_TIME_COMPARATOR);
addToScore(scoredProcessRecords, lruWeight);
}
if (rssWeight > 0.0f) {
- synchronized (mService.mAppProfiler.mProfilerLock) {
- Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
+ if (useFrequentRss) {
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, RSS_COMPARATOR);
+ } else {
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
+ }
}
addToScore(scoredProcessRecords, rssWeight);
}
if (usesWeight > 0.0f) {
- Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, CACHE_USE_COMPARATOR);
addToScore(scoredProcessRecords, usesWeight);
}
// Re-rank by the new combined score.
- Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+ SCORED_PROCESS_RECORD_COMPARATOR);
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
boolean printedHeader = false;
- for (int i = 0; i < scoredProcessRecords.length; ++i) {
+ for (int i = 0; i < numProcessesReRanked; ++i) {
if (scoredProcessRecords[i].proc.getPid()
!= lruList.get(lruPositions[i]).getPid()) {
if (!printedHeader) {
@@ -260,12 +410,18 @@
}
}
- for (int i = 0; i < scoredProcessRecords.length; ++i) {
+ for (int i = 0; i < numProcessesReRanked; ++i) {
lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
scoredProcessRecords[i].proc = null;
}
}
+ private static boolean appCanBeReRanked(ProcessRecord process) {
+ return !process.isKilledByAm()
+ && process.getThread() != null
+ && process.mState.getCurAdj() >= ProcessList.UNKNOWN_ADJ;
+ }
+
private static void addToScore(RankedProcessRecord[] scores, float weight) {
for (int i = 1; i < scores.length; ++i) {
scores[i].score += i * weight;
@@ -305,6 +461,16 @@
}
}
+ private static class RssComparator implements Comparator<RankedProcessRecord> {
+ @Override
+ public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
+ // High RSS first to match least recently used.
+ return Long.compare(
+ o2.proc.mState.getCacheOomRankerRss(),
+ o1.proc.mState.getCacheOomRankerRss());
+ }
+ }
+
private static class LastRssComparator implements Comparator<RankedProcessRecord> {
@Override
public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
@@ -317,4 +483,18 @@
public ProcessRecord proc;
public float score;
}
+
+ /**
+ * Interface for mocking {@link Process} static methods.
+ */
+ interface ProcessDependencies {
+ long[] getRss(int pid);
+ }
+
+ private static class ProcessDependenciesImpl implements ProcessDependencies {
+ @Override
+ public long[] getRss(int pid) {
+ return Process.getRss(pid);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index d4474d6..206dd88 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -343,6 +343,20 @@
private int mCacheOomRankerUseCount;
/**
+ * Process memory usage (RSS).
+ *
+ * Periodically populated by {@code CacheOomRanker}, stored in this object to cache the values.
+ */
+ @GuardedBy("mService")
+ private long mCacheOomRankerRss;
+
+ /**
+ * The last time, in milliseconds since boot, since {@link #mCacheOomRankerRss} was updated.
+ */
+ @GuardedBy("mService")
+ private long mCacheOomRankerRssTimeMs;
+
+ /**
* Whether or not this process is reachable from given process.
*/
@GuardedBy("mService")
@@ -577,6 +591,10 @@
@GuardedBy({"mService", "mProcLock"})
void setSetProcState(int setProcState) {
+ if (ActivityManager.isProcStateCached(mSetProcState)
+ && !ActivityManager.isProcStateCached(setProcState)) {
+ mCacheOomRankerUseCount++;
+ }
mSetProcState = setProcState;
}
@@ -840,12 +858,7 @@
@GuardedBy("mService")
void setCached(boolean cached) {
- if (mCached != cached) {
- mCached = cached;
- if (cached) {
- ++mCacheOomRankerUseCount;
- }
- }
+ mCached = cached;
}
@GuardedBy("mService")
@@ -1151,6 +1164,21 @@
return mLastInvisibleTime;
}
+ public void setCacheOomRankerRss(long rss, long rssTimeMs) {
+ mCacheOomRankerRss = rss;
+ mCacheOomRankerRssTimeMs = rssTimeMs;
+ }
+
+ @GuardedBy("mService")
+ public long getCacheOomRankerRss() {
+ return mCacheOomRankerRss;
+ }
+
+ @GuardedBy("mService")
+ public long getCacheOomRankerRssTimeMs() {
+ return mCacheOomRankerRssTimeMs;
+ }
+
@GuardedBy({"mService", "mProcLock"})
void dump(PrintWriter pw, String prefix, long nowUptime) {
if (mReportedInteraction || mFgInteractionTime != 0) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 804e442..17930ea 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -927,9 +927,9 @@
}
public void postNotification() {
- final int appUid = appInfo.uid;
- final int appPid = app.getPid();
- if (isForeground && foregroundNoti != null) {
+ if (isForeground && foregroundNoti != null && app != null) {
+ final int appUid = appInfo.uid;
+ final int appPid = app.getPid();
// Do asynchronous communication with notification manager to
// avoid deadlocks.
final String localPackageName = packageName;
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index db2ecc5..19dcee4 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -696,7 +696,7 @@
idpw.print("User Level Hibernation States, ");
idpw.printPair("user", userId);
idpw.println();
- Map<String, UserLevelState> stateMap = mUserStates.get(i);
+ Map<String, UserLevelState> stateMap = mUserStates.get(userId);
idpw.increaseIndent();
for (UserLevelState state : stateMap.values()) {
idpw.print(state);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8c19284..e09ba34 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2635,18 +2635,19 @@
case KeyEvent.KEYCODE_VOLUME_UP:
adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
- Binder.getCallingUid(), true, keyEventMode);
+ Binder.getCallingUid(), Binder.getCallingPid(), true, keyEventMode);
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
- Binder.getCallingUid(), true, keyEventMode);
+ Binder.getCallingUid(), Binder.getCallingPid(), true, keyEventMode);
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
adjustSuggestedStreamVolume(AudioManager.ADJUST_TOGGLE_MUTE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
- Binder.getCallingUid(), true, VOL_ADJUST_NORMAL);
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ true, VOL_ADJUST_NORMAL);
}
break;
default:
@@ -2659,8 +2660,8 @@
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
- caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(),
- VOL_ADJUST_NORMAL);
+ caller, Binder.getCallingUid(), Binder.getCallingPid(),
+ callingHasAudioSettingsPermission(), VOL_ADJUST_NORMAL);
}
public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) {
@@ -2686,7 +2687,7 @@
}
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
- String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
+ String callingPackage, String caller, int uid, int pid, boolean hasModifyAudioSettings,
int keyEventMode) {
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType
+ ", flags=" + flags + ", caller=" + caller
@@ -2759,7 +2760,7 @@
if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
}
- adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,
+ adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid, pid,
hasModifyAudioSettings, keyEventMode);
}
@@ -2791,12 +2792,12 @@
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
direction/*val1*/, flags/*val2*/, callingPackage));
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), callingHasAudioSettingsPermission(),
- VOL_ADJUST_NORMAL);
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ callingHasAudioSettingsPermission(), VOL_ADJUST_NORMAL);
}
protected void adjustStreamVolume(int streamType, int direction, int flags,
- String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
+ String callingPackage, String caller, int uid, int pid, boolean hasModifyAudioSettings,
int keyEventMode) {
if (mUseFixedVolume) {
return;
@@ -2818,8 +2819,7 @@
if (isMuteAdjust &&
(streamType == AudioSystem.STREAM_VOICE_CALL ||
streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&
- mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
+ mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -2829,8 +2829,8 @@
// If the stream is STREAM_ASSISTANT,
// make sure that the calling app have the MODIFY_AUDIO_ROUTING permission.
if (streamType == AudioSystem.STREAM_ASSISTANT &&
- mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ mContext.checkPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -4014,7 +4014,7 @@
}
private void setMasterMuteInternal(boolean mute, int flags, String callingPackage, int uid,
- int userId) {
+ int userId, int pid) {
// If we are being called by the system check for user we are going to change
// so we handle user restrictions correctly.
if (uid == android.os.Process.SYSTEM_UID) {
@@ -4025,8 +4025,8 @@
return;
}
if (userId != UserHandle.getCallingUserId() &&
- mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ mContext.checkPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
@@ -4066,7 +4066,7 @@
public void setMasterMute(boolean mute, int flags, String callingPackage, int userId) {
enforceModifyAudioRoutingPermission();
setMasterMuteInternal(mute, flags, callingPackage, Binder.getCallingUid(),
- userId);
+ userId, Binder.getCallingPid());
}
/** @see AudioManager#getStreamVolume(int) */
@@ -4967,8 +4967,8 @@
// direction and stream type swap here because the public
// adjustSuggested has a different order than the other methods.
- adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid,
- hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
+ adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName,
+ uid, pid, hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
}
/** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4987,7 +4987,7 @@
.toString()));
}
- adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
+ adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid, pid,
hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
}
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index bb627e5..00cb280 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -36,7 +36,16 @@
public static final String TAG = "AudioService.FadeOutManager";
+ /** duration of the fade out curve */
/*package*/ static final long FADE_OUT_DURATION_MS = 2000;
+ /**
+ * delay after which a faded out player will be faded back in. This will be heard by the user
+ * only in the case of unmuting players that didn't respect audio focus and didn't stop/pause
+ * when their app lost focus.
+ * This is the amount of time between the app being notified of
+ * the focus loss (when its muted by the fade out), and the time fade in (to unmute) starts
+ */
+ /*package*/ static final long DELAY_FADE_IN_OFFENDERS_MS = 2000;
private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;
@@ -148,6 +157,11 @@
}
}
+ /**
+ * Remove the app for the given UID from the list of faded out apps, unfade out its players
+ * @param uid the uid for the app to unfade out
+ * @param players map of current available players (so we can get an APC from piid)
+ */
synchronized void unfadeOutUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
Log.i(TAG, "unfadeOutUid() uid:" + uid);
final FadedOutApp fa = mFadedApps.remove(uid);
@@ -157,12 +171,6 @@
fa.removeUnfadeAll(players);
}
- synchronized void forgetUid(int uid) {
- //Log.v(TAG, "forget() uid:" + uid);
- //mFadedApps.remove(uid);
- // TODO unfade all players later in case they are reused or the app continued to play
- }
-
// pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
// see {@link PlaybackActivityMonitor#playerEvent}
synchronized void checkFade(@NonNull AudioPlaybackConfiguration apc) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index e6c4abfa..9548ada 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -131,6 +131,11 @@
@Override
public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
mFocusEnforcer.restoreVShapedPlayers(winner);
+ // remove scheduled events to unfade out offending players (if any) corresponding to
+ // this uid, as we're removing any effects of muting/ducking/fade out now
+ mFocusHandler.removeEqualMessages(MSL_L_FORGET_UID,
+ new ForgetFadeUidInfo(winner.getClientUid()));
+
}
@Override
@@ -1182,6 +1187,13 @@
mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser),
FadeOutManager.FADE_OUT_DURATION_MS);
}
+
+ private void postForgetUidLater(int uid) {
+ mFocusHandler.sendMessageDelayed(
+ mFocusHandler.obtainMessage(MSL_L_FORGET_UID, new ForgetFadeUidInfo(uid)),
+ FadeOutManager.DELAY_FADE_IN_OFFENDERS_MS);
+ }
+
//=================================================================
// Message handling
private Handler mFocusHandler;
@@ -1196,6 +1208,8 @@
*/
private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1;
+ private static final int MSL_L_FORGET_UID = 2;
+
private void initFocusThreading() {
mFocusThread = new HandlerThread(TAG);
mFocusThread.start();
@@ -1213,15 +1227,56 @@
if (loser.isInFocusLossLimbo()) {
loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
loser.release();
- mFocusEnforcer.forgetUid(loser.getClientUid());
+ postForgetUidLater(loser.getClientUid());
}
}
break;
+
+ case MSL_L_FORGET_UID:
+ final int uid = ((ForgetFadeUidInfo) msg.obj).mUid;
+ if (DEBUG) {
+ Log.d(TAG, "MSL_L_FORGET_UID uid=" + uid);
+ }
+ mFocusEnforcer.forgetUid(uid);
+ break;
default:
break;
}
}
};
+ }
+ /**
+ * Class to associate a UID with a scheduled event to "forget" a UID for the fade out behavior.
+ * Having a class with an equals() override allows using Handler.removeEqualsMessage() to
+ * unschedule events when needed. Here we need to unschedule the "unfading out" == "forget uid"
+ * whenever a new, more recent, focus related event happens before this one is handled.
+ */
+ private static final class ForgetFadeUidInfo {
+ private final int mUid;
+
+ ForgetFadeUidInfo(int uid) {
+ mUid = uid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ForgetFadeUidInfo f = (ForgetFadeUidInfo) o;
+ if (f.mUid != mUid) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mUid;
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index a13b2eb..b94cea4 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -747,7 +747,11 @@
@Override
public void forgetUid(int uid) {
- mFadingManager.forgetUid(uid);
+ final HashMap<Integer, AudioPlaybackConfiguration> players;
+ synchronized (mPlayerLock) {
+ players = (HashMap<Integer, AudioPlaybackConfiguration>) mPlayers.clone();
+ }
+ mFadingManager.unfadeOutUid(uid, players);
}
//=================================================================
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
index a81213d..11dc1db 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -175,29 +175,25 @@
/**
* Parses the given {@code configStr}, that is expected to be a comma separated list of changes
- * overrides, and returns a {@link PackageOverrides}.
+ * overrides, and returns a map from change ID to {@link PackageOverride} instances to add.
*
* <p>Each change override is in the following format:
- * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled?>'. If <enabled> is empty,
- * this indicates that any override for the specified change ID should be removed.
+ * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled>'.
*
* <p>If there are multiple overrides that should be added with the same change ID, the one
* that best fits the given {@code versionCode} is added.
*
* <p>Any overrides whose change ID is in {@code changeIdsToSkip} are ignored.
*
- * <p>If a change override entry in {@code configStr} is invalid, it will be ignored. If the
- * same change ID is both added and removed, i.e., has a change override entry with an empty
- * enabled and another with a non-empty enabled, the change ID will only be removed.
+ * <p>If a change override entry in {@code configStr} is invalid, it will be ignored.
*/
- static PackageOverrides parsePackageOverrides(
- String configStr, long versionCode, Set<Long> changeIdsToSkip) {
+ static Map<Long, PackageOverride> parsePackageOverrides(String configStr, long versionCode,
+ Set<Long> changeIdsToSkip) {
if (configStr.isEmpty()) {
- return new PackageOverrides();
+ return emptyMap();
}
PackageOverrideComparator comparator = new PackageOverrideComparator(versionCode);
Map<Long, PackageOverride> overridesToAdd = new ArrayMap<>();
- Set<Long> overridesToRemove = new ArraySet<>();
for (String overrideEntryString : configStr.split(",")) {
List<String> changeIdAndVersions = Arrays.asList(overrideEntryString.split(":", 4));
if (changeIdAndVersions.size() != 4) {
@@ -220,16 +216,6 @@
String maxVersionCodeStr = changeIdAndVersions.get(2);
String enabledStr = changeIdAndVersions.get(3);
- if (enabledStr.isEmpty()) {
- if (!minVersionCodeStr.isEmpty() || !maxVersionCodeStr.isEmpty()) {
- Slog.w(
- TAG,
- "min/max version code should be empty if enabled is empty: "
- + overrideEntryString);
- }
- overridesToRemove.add(changeId);
- continue;
- }
if (!BOOLEAN_PATTERN.matcher(enabledStr).matches()) {
Slog.w(TAG, "Invalid enabled string in override entry: " + overrideEntryString);
continue;
@@ -262,39 +248,7 @@
}
}
- for (Long changeId : overridesToRemove) {
- if (overridesToAdd.containsKey(changeId)) {
- Slog.w(
- TAG,
- "Change ID ["
- + changeId
- + "] is both added and removed in package override flag: "
- + configStr);
- overridesToAdd.remove(changeId);
- }
- }
-
- return new PackageOverrides(overridesToAdd, overridesToRemove);
- }
-
- /**
- * A container for a map from change ID to {@link PackageOverride} to add and a set of change
- * IDs to remove overrides for.
- *
- * <p>The map of overrides to add and the set of overrides to remove are mutually exclusive.
- */
- static final class PackageOverrides {
- public final Map<Long, PackageOverride> overridesToAdd;
- public final Set<Long> overridesToRemove;
-
- PackageOverrides() {
- this(emptyMap(), emptySet());
- }
-
- PackageOverrides(Map<Long, PackageOverride> overridesToAdd, Set<Long> overridesToRemove) {
- this.overridesToAdd = overridesToAdd;
- this.overridesToRemove = overridesToRemove;
- }
+ return overridesToAdd;
}
/**
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
index 63ae1af..6aed4b0 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -49,7 +49,6 @@
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.server.SystemService;
-import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
import java.util.ArrayList;
import java.util.Arrays;
@@ -128,20 +127,25 @@
}
/**
- * Same as {@link #applyOverrides(Properties, Map)} except all properties of the given {@code
- * namespace} are fetched via {@link DeviceConfig#getProperties}.
+ * Same as {@link #applyOverrides(Properties, Set, Map)} except all properties of the given
+ * {@code namespace} are fetched via {@link DeviceConfig#getProperties}.
*/
- private void applyAllOverrides(String namespace,
+ private void applyAllOverrides(String namespace, Set<Long> ownedChangeIds,
Map<String, Set<Long>> packageToChangeIdsToSkip) {
- applyOverrides(DeviceConfig.getProperties(namespace), packageToChangeIdsToSkip);
+ applyOverrides(DeviceConfig.getProperties(namespace), ownedChangeIds,
+ packageToChangeIdsToSkip);
}
/**
* Iterates all package override flags in the given {@code properties}, and for each flag whose
- * package is installed on the device, parses its value and applies the overrides in it with
+ * package is installed on the device, parses its value and adds the overrides in it with
* respect to the package's current installed version.
+ *
+ * <p>In addition, for each package, removes any override that wasn't just added, whose change
+ * ID is in {@code ownedChangeIds} but not in the respective set in {@code
+ * packageToChangeIdsToSkip}.
*/
- private void applyOverrides(Properties properties,
+ private void applyOverrides(Properties properties, Set<Long> ownedChangeIds,
Map<String, Set<Long>> packageToChangeIdsToSkip) {
Set<String> packageNames = new ArraySet<>(properties.getKeyset());
packageNames.remove(FLAG_OWNED_CHANGE_IDS);
@@ -154,15 +158,16 @@
}
applyPackageOverrides(properties.getString(packageName, /* defaultValue= */ ""),
- packageName, versionCode,
- packageToChangeIdsToSkip.getOrDefault(packageName, emptySet()));
+ packageName, versionCode, ownedChangeIds,
+ packageToChangeIdsToSkip.getOrDefault(packageName, emptySet()),
+ /* removeOtherOwnedOverrides= */ true);
}
}
/**
- * Applies all overrides in all supported namespaces for the given {@code packageName}.
+ * Adds all overrides in all supported namespaces for the given {@code packageName}.
*/
- private void applyAllPackageOverrides(String packageName) {
+ private void addAllPackageOverrides(String packageName) {
Long versionCode = getVersionCodeOrNull(packageName);
if (versionCode == null) {
return;
@@ -171,26 +176,40 @@
for (String namespace : mSupportedNamespaces) {
// We apply overrides for each namespace separately so that if there is a failure for
// one namespace, the other namespaces won't be affected.
+ Set<Long> ownedChangeIds = getOwnedChangeIds(namespace);
applyPackageOverrides(
DeviceConfig.getString(namespace, packageName, /* defaultValue= */ ""),
- packageName, versionCode,
- getOverridesToRemove(namespace).getOrDefault(packageName, emptySet()));
+ packageName, versionCode, ownedChangeIds,
+ getOverridesToRemove(namespace, ownedChangeIds).getOrDefault(packageName,
+ emptySet()), /* removeOtherOwnedOverrides */ false);
}
}
/**
- * Calls {@link AppCompatOverridesParser#parsePackageOverrides} on the given arguments, adds the
- * resulting {@link PackageOverrides#overridesToAdd} via {@link
- * IPlatformCompat#putOverridesOnReleaseBuilds}, and removes the resulting {@link
- * PackageOverrides#overridesToRemove} via {@link
- * IPlatformCompat#removeOverridesOnReleaseBuilds}.
+ * Calls {@link AppCompatOverridesParser#parsePackageOverrides} on the given arguments and adds
+ * the resulting overrides via {@link IPlatformCompat#putOverridesOnReleaseBuilds}.
+ *
+ * <p>In addition, if {@code removeOtherOwnedOverrides} is true, removes any override that
+ * wasn't just added, whose change ID is in {@code ownedChangeIds} but not in {@code
+ * changeIdsToSkip}, via {@link IPlatformCompat#removeOverridesOnReleaseBuilds}.
*/
- private void applyPackageOverrides(String configStr, String packageName,
- long versionCode, Set<Long> changeIdsToSkip) {
- PackageOverrides packageOverrides = AppCompatOverridesParser.parsePackageOverrides(
+ private void applyPackageOverrides(String configStr, String packageName, long versionCode,
+ Set<Long> ownedChangeIds, Set<Long> changeIdsToSkip,
+ boolean removeOtherOwnedOverrides) {
+ Map<Long, PackageOverride> overridesToAdd = AppCompatOverridesParser.parsePackageOverrides(
configStr, versionCode, changeIdsToSkip);
- putPackageOverrides(packageName, packageOverrides.overridesToAdd);
- removePackageOverrides(packageName, packageOverrides.overridesToRemove);
+ putPackageOverrides(packageName, overridesToAdd);
+
+ if (!removeOtherOwnedOverrides) {
+ return;
+ }
+ Set<Long> overridesToRemove = new ArraySet<>();
+ for (Long changeId : ownedChangeIds) {
+ if (!overridesToAdd.containsKey(changeId) && !changeIdsToSkip.contains(changeId)) {
+ overridesToRemove.add(changeId);
+ }
+ }
+ removePackageOverrides(packageName, overridesToRemove);
}
/**
@@ -227,10 +246,11 @@
* {@code namespace} and parses it into a map from package name to a set of change IDs to
* remove for that package.
*/
- private Map<String, Set<Long>> getOverridesToRemove(String namespace) {
+ private Map<String, Set<Long>> getOverridesToRemove(String namespace,
+ Set<Long> ownedChangeIds) {
return mOverridesParser.parseRemoveOverrides(
DeviceConfig.getString(namespace, FLAG_REMOVE_OVERRIDES, /* defaultValue= */ ""),
- getOwnedChangeIds(namespace));
+ ownedChangeIds);
}
/**
@@ -333,7 +353,9 @@
boolean ownedChangedIdsFlagChanged = properties.getKeyset().contains(
FLAG_OWNED_CHANGE_IDS);
- Map<String, Set<Long>> overridesToRemove = getOverridesToRemove(mNamespace);
+ Set<Long> ownedChangeIds = getOwnedChangeIds(mNamespace);
+ Map<String, Set<Long>> overridesToRemove = getOverridesToRemove(mNamespace,
+ ownedChangeIds);
if (removeOverridesFlagChanged || ownedChangedIdsFlagChanged) {
// In both cases it's possible that overrides that weren't removed before should
// now be removed.
@@ -343,9 +365,9 @@
if (removeOverridesFlagChanged) {
// We need to re-apply all overrides in the namespace since the remove overrides
// flag might have blocked some of them from being applied before.
- applyAllOverrides(mNamespace, overridesToRemove);
+ applyAllOverrides(mNamespace, ownedChangeIds, overridesToRemove);
} else {
- applyOverrides(properties, overridesToRemove);
+ applyOverrides(properties, ownedChangeIds, overridesToRemove);
}
}
}
@@ -392,7 +414,7 @@
switch (action) {
case ACTION_PACKAGE_ADDED:
case ACTION_PACKAGE_CHANGED:
- applyAllPackageOverrides(packageName);
+ addAllPackageOverrides(packageName);
break;
case ACTION_PACKAGE_REMOVED:
if (!isInstalledForAnyUser(packageName)) {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 5797b06..5fc301e 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -53,6 +53,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -77,7 +78,6 @@
import java.util.Locale;
import java.util.Objects;
-
/**
* The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
* picked by the system based on system-wide and display-specific configuration.
@@ -92,6 +92,8 @@
private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
+ private static final int MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED = 7;
+ private static final int MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED = 8;
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@@ -161,9 +163,10 @@
}
};
mSensorObserver = new SensorObserver(context, ballotBox, injector);
- mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, ballotBox);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
+ mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(),
+ mDeviceConfigDisplaySettings);
mDeviceConfig = injector.getDeviceConfig();
mAlwaysRespectAppRequest = false;
}
@@ -724,6 +727,11 @@
}
@VisibleForTesting
+ HbmObserver getHbmObserver() {
+ return mHbmObserver;
+ }
+
+ @VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
synchronized (mLock) {
@@ -792,6 +800,19 @@
(DesiredDisplayModeSpecsListener) msg.obj;
desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged();
break;
+
+ case MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED: {
+ int refreshRateInHbmSunlight = msg.arg1;
+ mHbmObserver.onDeviceConfigRefreshRateInHbmSunlightChanged(
+ refreshRateInHbmSunlight);
+ break;
+ }
+
+ case MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED: {
+ int refreshRateInHbmHdr = msg.arg1;
+ mHbmObserver.onDeviceConfigRefreshRateInHbmHdrChanged(refreshRateInHbmHdr);
+ break;
+ }
}
}
}
@@ -918,16 +939,19 @@
// result is a range.
public static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
+ // High-brightness-mode may need a specific range of refresh-rates to function properly.
+ public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
+
// SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
// It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
- public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;
+ public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 3;
// APP_REQUEST_REFRESH_RATE_RANGE is used to for internal apps to limit the refresh
// rate in certain cases, mostly to preserve power.
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
- public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 3;
+ public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 4;
// We split the app request into different priorities in case we can satisfy one desire
// without the other.
@@ -942,27 +966,24 @@
// The preferred refresh rate is set on the main surface of the app outside of
// DisplayModeDirector.
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
- public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 4;
- public static final int PRIORITY_APP_REQUEST_SIZE = 5;
+ public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
+ public static final int PRIORITY_APP_REQUEST_SIZE = 6;
// SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
// of low priority voters. It votes [0, max(PEAK, MIN)]
- public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6;
+ public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 7;
// LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
- public static final int PRIORITY_LOW_POWER_MODE = 7;
+ public static final int PRIORITY_LOW_POWER_MODE = 8;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
+ public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 9;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- public static final int PRIORITY_SKIN_TEMPERATURE = 9;
-
- // High-brightness-mode may need a specific range of refresh-rates to function properly.
- public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 10;
+ public static final int PRIORITY_SKIN_TEMPERATURE = 10;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
@@ -2258,33 +2279,78 @@
* HBM that are associated with that display. Restrictions are retrieved from
* DisplayManagerInternal but originate in the display-device-config file.
*/
- private static class HbmObserver implements DisplayManager.DisplayListener {
+ public static class HbmObserver implements DisplayManager.DisplayListener {
private final BallotBox mBallotBox;
private final Handler mHandler;
- private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray();
+ private final SparseIntArray mHbmMode = new SparseIntArray();
private final Injector mInjector;
+ private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
+ private int mRefreshRateInHbmSunlight;
+ private int mRefreshRateInHbmHdr;
private DisplayManagerInternal mDisplayManagerInternal;
- HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) {
+ HbmObserver(Injector injector, BallotBox ballotBox, Handler handler,
+ DeviceConfigDisplaySettings displaySettings) {
mInjector = injector;
mBallotBox = ballotBox;
mHandler = handler;
+ mDeviceConfigDisplaySettings = displaySettings;
}
public void observe() {
+ mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings.getRefreshRateInHbmSunlight();
+ mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings.getRefreshRateInHbmHdr();
+
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInjector.registerDisplayListener(this, mHandler,
DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
}
+ /**
+ * @return the refresh to lock to when the device is in high brightness mode for Sunlight.
+ */
+ @VisibleForTesting
+ int getRefreshRateInHbmSunlight() {
+ return mRefreshRateInHbmSunlight;
+ }
+
+ /**
+ * @return the refresh to lock to when the device is in high brightness mode for HDR.
+ */
+ @VisibleForTesting
+ int getRefreshRateInHbmHdr() {
+ return mRefreshRateInHbmHdr;
+ }
+
+ /**
+ * Recalculates the HBM vote when the device config has been changed.
+ */
+ public void onDeviceConfigRefreshRateInHbmSunlightChanged(int refreshRate) {
+ if (refreshRate != mRefreshRateInHbmSunlight) {
+ mRefreshRateInHbmSunlight = refreshRate;
+ onDeviceConfigRefreshRateInHbmChanged();
+ }
+ }
+
+ /**
+ * Recalculates the HBM vote when the device config has been changed.
+ */
+ public void onDeviceConfigRefreshRateInHbmHdrChanged(int refreshRate) {
+ if (refreshRate != mRefreshRateInHbmHdr) {
+ mRefreshRateInHbmHdr = refreshRate;
+ onDeviceConfigRefreshRateInHbmChanged();
+ }
+ }
+
@Override
public void onDisplayAdded(int displayId) {}
@Override
public void onDisplayRemoved(int displayId) {
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
+ mHbmMode.delete(displayId);
}
@Override
@@ -2294,31 +2360,56 @@
// Display no longer there. Assume we'll get an onDisplayRemoved very soon.
return;
}
- final boolean isHbmEnabled =
- info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
- if (isHbmEnabled == mHbmEnabled.get(displayId)) {
+ final int hbmMode = info.highBrightnessMode;
+ if (hbmMode == mHbmMode.get(displayId)) {
// no change, ignore.
return;
}
+ mHbmMode.put(displayId, hbmMode);
+ recalculateVotesForDisplay(displayId);
+ }
+
+ private void onDeviceConfigRefreshRateInHbmChanged() {
+ final int[] displayIds = mHbmMode.copyKeys();
+ if (displayIds != null) {
+ for (int id : displayIds) {
+ recalculateVotesForDisplay(id);
+ }
+ }
+ }
+
+ private void recalculateVotesForDisplay(int displayId) {
+ final int hbmMode = mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
Vote vote = null;
- mHbmEnabled.put(displayId, isHbmEnabled);
- if (isHbmEnabled) {
- final List<RefreshRateLimitation> limits =
+ if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
+ // Device resource properties take priority over DisplayDeviceConfig
+ if (mRefreshRateInHbmSunlight > 0) {
+ vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight,
+ mRefreshRateInHbmSunlight);
+ } else {
+ final List<RefreshRateLimitation> limits =
mDisplayManagerInternal.getRefreshRateLimitations(displayId);
- for (int i = 0; limits != null && i < limits.size(); i++) {
- final RefreshRateLimitation limitation = limits.get(i);
- if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
- vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
- break;
+ for (int i = 0; limits != null && i < limits.size(); i++) {
+ final RefreshRateLimitation limitation = limits.get(i);
+ if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
+ vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
+ break;
+ }
}
}
}
+ if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
+ && mRefreshRateInHbmHdr > 0) {
+ vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
+ }
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
}
void dumpLocked(PrintWriter pw) {
pw.println(" HbmObserver");
- pw.println(" mHbmEnabled: " + mHbmEnabled);
+ pw.println(" mHbmMode: " + mHbmMode);
+ pw.println(" mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight);
+ pw.println(" mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr);
}
}
@@ -2437,6 +2528,29 @@
return refreshRate;
}
+ public int getRefreshRateInHbmSunlight() {
+ final int defaultRefreshRateInHbmSunlight =
+ mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInHbmSunlight);
+
+ final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
+ defaultRefreshRateInHbmSunlight);
+
+ return refreshRate;
+ }
+
+ public int getRefreshRateInHbmHdr() {
+ final int defaultRefreshRateInHbmHdr =
+ mContext.getResources().getInteger(R.integer.config_defaultRefreshRateInHbmHdr);
+
+ final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
+ defaultRefreshRateInHbmHdr);
+
+ return refreshRate;
+ }
+
/*
* Return null if no such property
*/
@@ -2476,6 +2590,15 @@
.sendToTarget();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
.sendToTarget();
+
+ final int refreshRateInHbmSunlight = getRefreshRateInHbmSunlight();
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
+ refreshRateInHbmSunlight, 0)
+ .sendToTarget();
+
+ final int refreshRateInHbmHdr = getRefreshRateInHbmHdr();
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0)
+ .sendToTarget();
}
private int[] getIntArrayProperty(String prop) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index abbe13a..1063481 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -721,13 +721,13 @@
final IBinder token = device.getDisplayTokenLocked();
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
mHandler.post(() -> {
- if (mDisplayDevice == device) {
- return;
+ if (mDisplayDevice != device) {
+ mDisplayDevice = device;
+ mUniqueDisplayId = uniqueId;
+ mDisplayDeviceConfig = config;
+ loadFromDisplayDeviceConfig(token, info);
+ updatePowerState();
}
- mDisplayDevice = device;
- mUniqueDisplayId = uniqueId;
- mDisplayDeviceConfig = config;
- loadFromDisplayDeviceConfig(token, info);
});
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 973dcc4..0fbc3e8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -22,6 +22,8 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -69,7 +71,7 @@
public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
- private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 500;
+ private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 300;
private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
@@ -98,6 +100,11 @@
private final boolean mSupportsConcurrentInternalDisplays;
/**
+ * Wake the device when transitioning into this device state.
+ */
+ private final int mDeviceStateOnWhichToWakeUp;
+
+ /**
* Map of all logical displays indexed by logical display id.
* Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
* TODO: multi-display - Move the aforementioned comment?
@@ -113,6 +120,7 @@
private final Listener mListener;
private final DisplayManagerService.SyncRoot mSyncRoot;
private final LogicalDisplayMapperHandler mHandler;
+ private final PowerManager mPowerManager;
/**
* Has an entry for every logical display that the rest of the system has been notified about.
@@ -150,12 +158,15 @@
@NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
@NonNull Handler handler) {
mSyncRoot = syncRoot;
+ mPowerManager = context.getSystemService(PowerManager.class);
mHandler = new LogicalDisplayMapperHandler(handler.getLooper());
mDisplayDeviceRepo = repo;
mListener = listener;
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
mSupportsConcurrentInternalDisplays = context.getResources().getBoolean(
com.android.internal.R.bool.config_supportsConcurrentInternalDisplays);
+ mDeviceStateOnWhichToWakeUp = context.getResources().getInteger(
+ com.android.internal.R.integer.config_deviceStateOnWhichToWakeUp);
mDisplayDeviceRepo.addListener(this);
mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
}
@@ -318,6 +329,7 @@
ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
ipw.println("mCurrentLayout=" + mCurrentLayout);
+ ipw.println("mDeviceStateOnWhichToWakeUp=" + mDeviceStateOnWhichToWakeUp);
final int logicalDisplayCount = mLogicalDisplays.size();
ipw.println();
@@ -335,7 +347,9 @@
}
void setDeviceStateLocked(int state) {
- Slog.i(TAG, "Requesting Transition to state: " + state);
+ final boolean isInteractive = mPowerManager.isInteractive();
+ Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
+ + ", interactive=" + isInteractive);
// As part of a state transition, we may need to turn off some displays temporarily so that
// the transition is smooth. Plus, on some devices, only one internal displays can be
// on at a time. We use DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be
@@ -344,8 +358,13 @@
resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION);
}
mPendingDeviceState = state;
- if (areAllTransitioningDisplaysOffLocked()) {
- // Nothing to wait on, we're good to go
+ final boolean wakeDevice = mPendingDeviceState == mDeviceStateOnWhichToWakeUp
+ && !isInteractive;
+
+ // If all displays are off already, we can just transition here, unless the device is asleep
+ // and we plan on waking it up. In that case, fall through to the call to wakeUp, and defer
+ // the final transition until later once the device is awake.
+ if (areAllTransitioningDisplaysOffLocked() && !wakeDevice) {
transitionToPendingStateLocked();
return;
}
@@ -356,6 +375,14 @@
// Send the transitioning phase updates to DisplayManager so that the displays can
// start turning OFF in preparation for the new layout.
updateLogicalDisplaysLocked();
+
+ if (wakeDevice) {
+ // We already told the displays to turn off, now we need to wake the device as
+ // we transition to this new state. We do it here so that the waking happens between the
+ // transition from one layout to another.
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_UNFOLD_DEVICE,
+ "server.display:unfold");
+ }
mHandler.sendEmptyMessageDelayed(MSG_TRANSITION_TO_PENDING_DEVICE_STATE,
TIMEOUT_STATE_TRANSITION_MILLIS);
}
@@ -482,6 +509,7 @@
assignDisplayGroupLocked(display);
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
+ // The display is involved in a display layout transition
} else if (updateState == UPDATE_STATE_TRANSITION) {
mLogicalDisplaysToUpdate.put(displayId,
LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
@@ -672,14 +700,14 @@
// We consider a display-device as changing/transition if
// 1) It's already marked as transitioning
- // 2) It's going from enabled to disabled
+ // 2) It's going from enabled to disabled, or vice versa
// 3) It's enabled, but it's mapped to a new logical display ID. To the user this
// would look like apps moving from one screen to another since task-stacks stay
// with the logical display [ID].
final boolean isTransitioning =
(logicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION)
- || (wasEnabled && !willBeEnabled)
- || (wasEnabled && deviceHasNewLogicalDisplayId);
+ || (wasEnabled != willBeEnabled)
+ || deviceHasNewLogicalDisplayId;
if (isTransitioning) {
setDisplayPhase(logicalDisplay, phase);
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 2227506..4d9253e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -444,12 +444,11 @@
capabilities = networkAttributes.mCapabilities;
Log.i(TAG, String.format(
"updateNetworkState, state=%s, connected=%s, network=%s, capabilities=%s"
- + ", apn: %s, availableNetworkCount: %d",
+ + ", availableNetworkCount: %d",
agpsDataConnStateAsString(),
isConnected,
network,
capabilities,
- apn,
mAvailableNetworkAttributes.size()));
if (native_is_agps_ril_supported()) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 62d8c32..345dc21 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -243,15 +243,24 @@
intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
}
+ // send() SHOULD only run the completion callback if it completes successfully. however,
+ // b/199464864 (which could not be fixed in the S timeframe) means that it's possible
+ // for send() to throw an exception AND run the completion callback. if this happens, we
+ // would over-release the wakelock... we take matters into our own hands to ensure that
+ // the completion callback can only be run if send() completes successfully. this means
+ // the completion callback may be run inline - but as we've never specified what thread
+ // the callback is run on, this is fine.
+ GatedCallback gatedCallback = new GatedCallback(onCompleteCallback);
+
mPendingIntent.send(
mContext,
0,
intent,
- onCompleteCallback != null ? (pI, i, rC, rD, rE) -> onCompleteCallback.run()
- : null,
+ (pI, i, rC, rD, rE) -> gatedCallback.run(),
null,
null,
options.toBundle());
+ gatedCallback.allow();
}
@Override
@@ -1960,11 +1969,6 @@
Preconditions.checkState(Thread.holdsLock(mLock));
}
- if (mDelayedRegister != null) {
- mAlarmHelper.cancel(mDelayedRegister);
- mDelayedRegister = null;
- }
-
// calculate how long the new request should be delayed before sending it off to the
// provider, under the assumption that once we send the request off, the provider will
// immediately attempt to deliver a new location satisfying that request.
@@ -1997,8 +2001,8 @@
public void onAlarm() {
synchronized (mLock) {
if (mDelayedRegister == this) {
- setProviderRequest(newRequest);
mDelayedRegister = null;
+ setProviderRequest(newRequest);
}
}
}
@@ -2025,6 +2029,11 @@
@GuardedBy("mLock")
void setProviderRequest(ProviderRequest request) {
+ if (mDelayedRegister != null) {
+ mAlarmHelper.cancel(mDelayedRegister);
+ mDelayedRegister = null;
+ }
+
EVENT_LOG.logProviderUpdateRequest(mName, request);
mProvider.getController().setRequest(request);
@@ -2734,4 +2743,49 @@
}
}
}
+
+ private static class GatedCallback implements Runnable {
+
+ private @Nullable Runnable mCallback;
+
+ @GuardedBy("this")
+ private boolean mGate;
+ @GuardedBy("this")
+ private boolean mRun;
+
+ GatedCallback(Runnable callback) {
+ mCallback = callback;
+ }
+
+ public void allow() {
+ Runnable callback = null;
+ synchronized (this) {
+ mGate = true;
+ if (mRun && mCallback != null) {
+ callback = mCallback;
+ mCallback = null;
+ }
+ }
+
+ if (callback != null) {
+ callback.run();
+ }
+ }
+
+ @Override
+ public void run() {
+ Runnable callback = null;
+ synchronized (this) {
+ mRun = true;
+ if (mGate && mCallback != null) {
+ callback = mCallback;
+ mCallback = null;
+ }
+ }
+
+ if (callback != null) {
+ callback.run();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 22a675a..ad87c45 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -105,20 +105,15 @@
synchronized (mLock) {
mDeviceIdleHelper.addListener(this);
- mDeviceIdle = mDeviceIdleHelper.isDeviceIdle();
- mDeviceStationaryHelper.addListener(this);
- mDeviceStationary = false;
- mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
-
- onThrottlingChangedLocked(false);
+ onDeviceIdleChanged(mDeviceIdleHelper.isDeviceIdle());
}
}
@Override
protected void onStop() {
synchronized (mLock) {
- mDeviceStationaryHelper.removeListener(this);
mDeviceIdleHelper.removeListener(this);
+ onDeviceIdleChanged(false);
mIncomingRequest = ProviderRequest.EMPTY_REQUEST;
mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
@@ -151,13 +146,26 @@
}
mDeviceIdle = deviceIdle;
- onThrottlingChangedLocked(false);
+
+ if (deviceIdle) {
+ // device stationary helper will deliver an immediate listener update
+ mDeviceStationaryHelper.addListener(this);
+ } else {
+ mDeviceStationaryHelper.removeListener(this);
+ mDeviceStationary = false;
+ mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
+ }
}
}
@Override
public void onDeviceStationaryChanged(boolean deviceStationary) {
synchronized (mLock) {
+ if (!mDeviceIdle) {
+ // stationary detection is only registered while idle - ignore late notifications
+ return;
+ }
+
if (mDeviceStationary == deviceStationary) {
return;
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index bccc52f..ddaaa1e 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1536,7 +1536,7 @@
@Override
public void onNullBinding(ComponentName name) {
Slog.v(TAG, "onNullBinding() called with: name = [" + name + "]");
- mServicesBound.remove(servicesBindingTag);
+ mContext.unbindService(this);
}
};
if (!mContext.bindServiceAsUser(intent,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 78c909d..7ba0f04 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5397,6 +5397,7 @@
== IMPORTANCE_NONE) {
cancelNotificationsFromListener(token, new String[]{r.getKey()});
} else {
+ r.setPendingLogUpdate(true);
needsSort = true;
}
}
@@ -8057,64 +8058,151 @@
}
}
+ static class NotificationRecordExtractorData {
+ // Class that stores any field in a NotificationRecord that can change via an extractor.
+ // Used to cache previous data used in a sort.
+ int mPosition;
+ int mVisibility;
+ boolean mShowBadge;
+ boolean mAllowBubble;
+ boolean mIsBubble;
+ NotificationChannel mChannel;
+ String mGroupKey;
+ ArrayList<String> mOverridePeople;
+ ArrayList<SnoozeCriterion> mSnoozeCriteria;
+ Integer mUserSentiment;
+ Integer mSuppressVisually;
+ ArrayList<Notification.Action> mSystemSmartActions;
+ ArrayList<CharSequence> mSmartReplies;
+ int mImportance;
+
+ // These fields may not trigger a reranking but diffs here may be logged.
+ float mRankingScore;
+ boolean mIsConversation;
+
+ NotificationRecordExtractorData(int position, int visibility, boolean showBadge,
+ boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey,
+ ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria,
+ Integer userSentiment, Integer suppressVisually,
+ ArrayList<Notification.Action> systemSmartActions,
+ ArrayList<CharSequence> smartReplies, int importance, float rankingScore,
+ boolean isConversation) {
+ mPosition = position;
+ mVisibility = visibility;
+ mShowBadge = showBadge;
+ mAllowBubble = allowBubble;
+ mIsBubble = isBubble;
+ mChannel = channel;
+ mGroupKey = groupKey;
+ mOverridePeople = overridePeople;
+ mSnoozeCriteria = snoozeCriteria;
+ mUserSentiment = userSentiment;
+ mSuppressVisually = suppressVisually;
+ mSystemSmartActions = systemSmartActions;
+ mSmartReplies = smartReplies;
+ mImportance = importance;
+ mRankingScore = rankingScore;
+ mIsConversation = isConversation;
+ }
+
+ // Returns whether the provided NotificationRecord differs from the cached data in any way.
+ // Should be guarded by mNotificationLock; not annotated here as this class is static.
+ boolean hasDiffForRankingLocked(NotificationRecord r, int newPosition) {
+ return mPosition != newPosition
+ || mVisibility != r.getPackageVisibilityOverride()
+ || mShowBadge != r.canShowBadge()
+ || mAllowBubble != r.canBubble()
+ || mIsBubble != r.getNotification().isBubbleNotification()
+ || !Objects.equals(mChannel, r.getChannel())
+ || !Objects.equals(mGroupKey, r.getGroupKey())
+ || !Objects.equals(mOverridePeople, r.getPeopleOverride())
+ || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria())
+ || !Objects.equals(mUserSentiment, r.getUserSentiment())
+ || !Objects.equals(mSuppressVisually, r.getSuppressedVisualEffects())
+ || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions())
+ || !Objects.equals(mSmartReplies, r.getSmartReplies())
+ || mImportance != r.getImportance();
+ }
+
+ // Returns whether the NotificationRecord has a change from this data for which we should
+ // log an update. This method specifically targets fields that may be changed via
+ // adjustments from the assistant.
+ //
+ // Fields here are the union of things in NotificationRecordLogger.shouldLogReported
+ // and NotificationRecord.applyAdjustments.
+ //
+ // Should be guarded by mNotificationLock; not annotated here as this class is static.
+ boolean hasDiffForLoggingLocked(NotificationRecord r, int newPosition) {
+ return mPosition != newPosition
+ || !Objects.equals(mChannel, r.getChannel())
+ || !Objects.equals(mGroupKey, r.getGroupKey())
+ || !Objects.equals(mOverridePeople, r.getPeopleOverride())
+ || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria())
+ || !Objects.equals(mUserSentiment, r.getUserSentiment())
+ || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions())
+ || !Objects.equals(mSmartReplies, r.getSmartReplies())
+ || mImportance != r.getImportance()
+ || !r.rankingScoreMatches(mRankingScore)
+ || mIsConversation != r.isConversation();
+ }
+ }
+
void handleRankingSort() {
if (mRankingHelper == null) return;
synchronized (mNotificationLock) {
final int N = mNotificationList.size();
// Any field that can change via one of the extractors needs to be added here.
- ArrayList<String> orderBefore = new ArrayList<>(N);
- int[] visibilities = new int[N];
- boolean[] showBadges = new boolean[N];
- boolean[] allowBubbles = new boolean[N];
- boolean[] isBubble = new boolean[N];
- ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
- ArrayList<String> groupKeyBefore = new ArrayList<>(N);
- ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
- ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
- ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
- ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
- ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N);
- ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
- int[] importancesBefore = new int[N];
+ ArrayMap<String, NotificationRecordExtractorData> extractorDataBefore =
+ new ArrayMap<>(N);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
- orderBefore.add(r.getKey());
- visibilities[i] = r.getPackageVisibilityOverride();
- showBadges[i] = r.canShowBadge();
- allowBubbles[i] = r.canBubble();
- isBubble[i] = r.getNotification().isBubbleNotification();
- channelBefore.add(r.getChannel());
- groupKeyBefore.add(r.getGroupKey());
- overridePeopleBefore.add(r.getPeopleOverride());
- snoozeCriteriaBefore.add(r.getSnoozeCriteria());
- userSentimentBefore.add(r.getUserSentiment());
- suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
- systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions());
- smartRepliesBefore.add(r.getSmartReplies());
- importancesBefore[i] = r.getImportance();
+ NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData(
+ i,
+ r.getPackageVisibilityOverride(),
+ r.canShowBadge(),
+ r.canBubble(),
+ r.getNotification().isBubbleNotification(),
+ r.getChannel(),
+ r.getGroupKey(),
+ r.getPeopleOverride(),
+ r.getSnoozeCriteria(),
+ r.getUserSentiment(),
+ r.getSuppressedVisualEffects(),
+ r.getSystemGeneratedSmartActions(),
+ r.getSmartReplies(),
+ r.getImportance(),
+ r.getRankingScore(),
+ r.isConversation());
+ extractorDataBefore.put(r.getKey(), extractorData);
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
- if (!orderBefore.get(i).equals(r.getKey())
- || visibilities[i] != r.getPackageVisibilityOverride()
- || showBadges[i] != r.canShowBadge()
- || allowBubbles[i] != r.canBubble()
- || isBubble[i] != r.getNotification().isBubbleNotification()
- || !Objects.equals(channelBefore.get(i), r.getChannel())
- || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
- || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
- || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
- || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
- || !Objects.equals(suppressVisuallyBefore.get(i),
- r.getSuppressedVisualEffects())
- || !Objects.equals(systemSmartActionsBefore.get(i),
- r.getSystemGeneratedSmartActions())
- || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())
- || importancesBefore[i] != r.getImportance()) {
+ if (!extractorDataBefore.containsKey(r.getKey())) {
+ // This shouldn't happen given that we just built this with all the
+ // notifications, but check just to be safe.
+ continue;
+ }
+ if (extractorDataBefore.get(r.getKey()).hasDiffForRankingLocked(r, i)) {
mHandler.scheduleSendRankingUpdate();
- return;
+ }
+
+ // If this notification is one for which we wanted to log an update, and
+ // sufficient relevant bits are different, log update.
+ if (r.hasPendingLogUpdate()) {
+ // We need to acquire the previous data associated with this specific
+ // notification, as the one at the current index may be unrelated if
+ // notification order has changed.
+ NotificationRecordExtractorData prevData = extractorDataBefore.get(r.getKey());
+ if (prevData.hasDiffForLoggingLocked(r, i)) {
+ mNotificationRecordLogger.logNotificationAdjusted(r, i, 0,
+ getGroupInstanceId(r.getSbn().getGroupKey()));
+ }
+
+ // Remove whether there was a diff or not; we've sorted the key, so if it
+ // turns out there was nothing to log, that's fine too.
+ r.setPendingLogUpdate(false);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f66cfa9..b4ca511 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -200,6 +200,10 @@
private boolean mIsAppImportanceLocked;
private ArraySet<Uri> mGrantableUris;
+ // Whether this notification record should have an update logged the next time notifications
+ // are sorted.
+ private boolean mPendingLogUpdate = false;
+
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel) {
this.sbn = sbn;
@@ -648,17 +652,23 @@
final ArrayList<String> people =
adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
setPeopleOverride(people);
+ EventLogTags.writeNotificationAdjusted(
+ getKey(), Adjustment.KEY_PEOPLE, people.toString());
}
if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
final ArrayList<SnoozeCriterion> snoozeCriterionList =
adjustment.getSignals().getParcelableArrayList(
Adjustment.KEY_SNOOZE_CRITERIA);
setSnoozeCriteria(snoozeCriterionList);
+ EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_SNOOZE_CRITERIA,
+ snoozeCriterionList.toString());
}
if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
final String groupOverrideKey =
adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
setOverrideGroupKey(groupOverrideKey);
+ EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_GROUP_KEY,
+ groupOverrideKey);
}
if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
// Only allow user sentiment update from assistant if user hasn't already
@@ -667,27 +677,42 @@
&& (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
setUserSentiment(adjustment.getSignals().getInt(
Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+ EventLogTags.writeNotificationAdjusted(getKey(),
+ Adjustment.KEY_USER_SENTIMENT,
+ Integer.toString(getUserSentiment()));
}
}
if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) {
setSystemGeneratedSmartActions(
signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS));
+ EventLogTags.writeNotificationAdjusted(getKey(),
+ Adjustment.KEY_CONTEXTUAL_ACTIONS,
+ getSystemGeneratedSmartActions().toString());
}
if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) {
setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES));
+ EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_TEXT_REPLIES,
+ getSmartReplies().toString());
}
if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
importance = Math.min(IMPORTANCE_HIGH, importance);
setAssistantImportance(importance);
+ EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_IMPORTANCE,
+ Integer.toString(importance));
}
if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) {
mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE);
+ EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_RANKING_SCORE,
+ Float.toString(mRankingScore));
}
if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) {
mIsNotConversationOverride = signals.getBoolean(
Adjustment.KEY_NOT_CONVERSATION);
+ EventLogTags.writeNotificationAdjusted(getKey(),
+ Adjustment.KEY_NOT_CONVERSATION,
+ Boolean.toString(mIsNotConversationOverride));
}
if (!signals.isEmpty() && adjustment.getIssuer() != null) {
mAdjustmentIssuer = adjustment.getIssuer();
@@ -1478,6 +1503,24 @@
return sbn;
}
+ /**
+ * Returns whether this record's ranking score is approximately equal to otherScore
+ * (the difference must be within 0.0001).
+ */
+ public boolean rankingScoreMatches(float otherScore) {
+ return Math.abs(mRankingScore - otherScore) < 0.0001;
+ }
+
+ protected void setPendingLogUpdate(boolean pendingLogUpdate) {
+ mPendingLogUpdate = pendingLogUpdate;
+ }
+
+ // If a caller of this function subsequently logs the update, they should also call
+ // setPendingLogUpdate to false to make sure other callers don't also do so.
+ protected boolean hasPendingLogUpdate() {
+ return mPendingLogUpdate;
+ }
+
@VisibleForTesting
static final class Light {
public final int color;
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 0e2ff75..7d8564f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -59,6 +59,20 @@
InstanceId groupId);
/**
+ * Logs a NotificationReported atom reflecting an adjustment to a notification.
+ * Unlike maybeLogNotificationPosted, this method is guaranteed to log a notification update,
+ * so the caller must take responsibility for checking that that logging update is necessary,
+ * and that the notification is meaningfully changed.
+ * @param r The NotificationRecord. If null, no action is taken.
+ * @param position The position at which this notification is ranked.
+ * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user.
+ * @param groupId The instance Id of the group summary notification, or null.
+ */
+ void logNotificationAdjusted(@Nullable NotificationRecord r,
+ int position, int buzzBeepBlink,
+ InstanceId groupId);
+
+ /**
* Logs a notification cancel / dismiss event using UiEventReported (event ids from the
* NotificationCancelledEvents enum).
* @param r The NotificationRecord. If null, no action is taken.
@@ -96,7 +110,9 @@
@UiEvent(doc = "New notification enqueued to post")
NOTIFICATION_POSTED(162),
@UiEvent(doc = "Notification substantially updated, or alerted again.")
- NOTIFICATION_UPDATED(163);
+ NOTIFICATION_UPDATED(163),
+ @UiEvent(doc = "Notification adjusted by assistant.")
+ NOTIFICATION_ADJUSTED(908);
private final int mId;
NotificationReportedEvent(int id) {
@@ -349,7 +365,8 @@
&& Objects.equals(r.getSbn().getNotification().category,
old.getSbn().getNotification().category)
&& (r.getImportance() == old.getImportance())
- && (getLoggingImportance(r) == getLoggingImportance(old)));
+ && (getLoggingImportance(r) == getLoggingImportance(old))
+ && r.rankingScoreMatches(old.getRankingScore()));
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 1a99fb0e..1859ec4 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import android.annotation.Nullable;
+
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
@@ -37,33 +39,49 @@
if (!p.shouldLogReported(buzzBeepBlink)) {
return;
}
+ writeNotificationReportedAtom(p, NotificationReportedEvent.fromRecordPair(p),
+ position, buzzBeepBlink, groupId);
+ }
+
+ @Override
+ public void logNotificationAdjusted(@Nullable NotificationRecord r,
+ int position, int buzzBeepBlink,
+ InstanceId groupId) {
+ NotificationRecordPair p = new NotificationRecordPair(r, null);
+ writeNotificationReportedAtom(p, NotificationReportedEvent.NOTIFICATION_ADJUSTED,
+ position, buzzBeepBlink, groupId);
+ }
+
+ private void writeNotificationReportedAtom(NotificationRecordPair p,
+ NotificationReportedEvent eventType, int position, int buzzBeepBlink,
+ InstanceId groupId) {
FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
- /* int32 event_id = 1 */ NotificationReportedEvent.fromRecordPair(p).getId(),
- /* int32 uid = 2 */ r.getUid(),
- /* string package_name = 3 */ r.getSbn().getPackageName(),
+ /* int32 event_id = 1 */ eventType.getId(),
+ /* int32 uid = 2 */ p.r.getUid(),
+ /* string package_name = 3 */ p.r.getSbn().getPackageName(),
/* int32 instance_id = 4 */ p.getInstanceId(),
/* int32 notification_id_hash = 5 */ p.getNotificationIdHash(),
/* int32 channel_id_hash = 6 */ p.getChannelIdHash(),
/* string group_id_hash = 7 */ p.getGroupIdHash(),
/* int32 group_instance_id = 8 */ (groupId == null) ? 0 : groupId.getId(),
- /* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(),
- /* string category = 10 */ r.getSbn().getNotification().category,
+ /* bool is_group_summary = 9 */ p.r.getSbn().getNotification().isGroupSummary(),
+ /* string category = 10 */ p.r.getSbn().getNotification().category,
/* int32 style = 11 */ p.getStyle(),
/* int32 num_people = 12 */ p.getNumPeople(),
/* int32 position = 13 */ position,
/* android.stats.sysui.NotificationImportance importance = 14 */
- NotificationRecordLogger.getLoggingImportance(r),
+ NotificationRecordLogger.getLoggingImportance(p.r),
/* int32 alerting = 15 */ buzzBeepBlink,
/* NotificationImportanceExplanation importance_source = 16 */
- r.getImportanceExplanationCode(),
+ p.r.getImportanceExplanationCode(),
/* android.stats.sysui.NotificationImportance importance_initial = 17 */
- r.getInitialImportance(),
+ p.r.getInitialImportance(),
/* NotificationImportanceExplanation importance_initial_source = 18 */
- r.getInitialImportanceExplanationCode(),
+ p.r.getInitialImportanceExplanationCode(),
/* android.stats.sysui.NotificationImportance importance_asst = 19 */
- r.getAssistantImportance(),
+ p.r.getAssistantImportance(),
/* int32 assistant_hash = 20 */ p.getAssistantHash(),
- /* float assistant_ranking_score = 21 */ r.getRankingScore()
+ /* float assistant_ranking_score = 21 */ p.r.getRankingScore()
);
}
diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
index 947405e..b276c6f 100644
--- a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
+++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
@@ -19,10 +19,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Build;
import android.os.IDeviceIdentifiersPolicyService;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.UserHandle;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.server.SystemService;
@@ -65,11 +68,31 @@
@Override
public @Nullable String getSerialForPackage(String callingPackage,
String callingFeatureId) throws RemoteException {
+ if (!checkPackageBelongsToCaller(callingPackage)) {
+ throw new IllegalArgumentException(
+ "Invalid callingPackage or callingPackage does not belong to caller's uid:"
+ + Binder.getCallingUid());
+ }
+
if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext,
callingPackage, callingFeatureId, "getSerial")) {
return Build.UNKNOWN;
}
return SystemProperties.get("ro.serialno", Build.UNKNOWN);
}
+
+ private boolean checkPackageBelongsToCaller(String callingPackage) {
+ int callingUid = Binder.getCallingUid();
+ int callingUserId = UserHandle.getUserId(callingUid);
+ int callingPackageUid;
+ try {
+ callingPackageUid = mContext.getPackageManager().getPackageUidAsUser(
+ callingPackage, callingUserId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ return callingPackageUid == callingUid;
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3691f60..1debaf3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -16163,8 +16163,7 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
- boolean suspended) {
+ void sendPackagesSuspendedForUser(String intent, String[] pkgList, int[] uidList, int userId) {
final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
@@ -16205,11 +16204,8 @@
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
? null : allowListsToSend.get(i);
- sendPackageBroadcast(
- suspended ? Intent.ACTION_PACKAGES_SUSPENDED
- : Intent.ACTION_PACKAGES_UNSUSPENDED,
- null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
- userIds, null, allowList, null);
+ sendPackageBroadcast(intent, null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null,
+ null, userIds, null, allowList, null);
}
}
@@ -16523,6 +16519,8 @@
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
+ final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
+ final IntArray modifiedUids = new IntArray(packageNames.length);
final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
final boolean[] canSuspend = suspended ? canSuspendPackageForUserInternal(packageNames,
userId) : null;
@@ -16550,13 +16548,14 @@
unactionedPackages.add(packageName);
continue;
}
- boolean packageUnsuspended;
+ final boolean packageUnsuspended;
+ final boolean packageModified;
synchronized (mLock) {
if (suspended) {
- pkgSetting.addOrUpdateSuspension(callingPackage, dialogInfo, appExtras,
- launcherExtras, userId);
+ packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
+ dialogInfo, appExtras, launcherExtras, userId);
} else {
- pkgSetting.removeSuspension(callingPackage, userId);
+ packageModified = pkgSetting.removeSuspension(callingPackage, userId);
}
packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
}
@@ -16564,18 +16563,29 @@
changedPackagesList.add(packageName);
changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
+ if (packageModified) {
+ modifiedPackagesList.add(packageName);
+ modifiedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+ }
}
if (!changedPackagesList.isEmpty()) {
- final String[] changedPackages = changedPackagesList.toArray(
- new String[changedPackagesList.size()]);
- sendPackagesSuspendedForUser(changedPackages, changedUids.toArray(), userId, suspended);
+ final String[] changedPackages = changedPackagesList.toArray(new String[0]);
+ sendPackagesSuspendedForUser(
+ suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+ : Intent.ACTION_PACKAGES_UNSUSPENDED,
+ changedPackages, changedUids.toArray(), userId);
sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
- return unactionedPackages.toArray(new String[unactionedPackages.size()]);
+ // Send the suspension changed broadcast to ensure suspension state is not stale.
+ if (!modifiedPackagesList.isEmpty()) {
+ sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+ modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
+ }
+ return unactionedPackages.toArray(new String[0]);
}
@Override
@@ -16704,7 +16714,8 @@
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
- sendPackagesSuspendedForUser(packageArray, unsuspendedUids.toArray(), userId, false);
+ sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+ packageArray, unsuspendedUids.toArray(), userId);
}
}
@@ -22646,8 +22657,9 @@
removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), userId, appId);
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+ StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
final int flags;
- if (umInternal.isUserUnlockingOrUnlocked(userId)) {
+ if (StorageManager.isUserKeyUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(userId)) {
flags = StorageManager.FLAG_STORAGE_DE;
@@ -25512,9 +25524,11 @@
// Reconcile app data for all started/unlocked users
final StorageManager sm = mInjector.getSystemService(StorageManager.class);
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+ StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
for (UserInfo user : mUserManager.getUsers(false /* includeDying */)) {
final int flags;
- if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ if (StorageManager.isUserKeyUnlocked(user.id)
+ && smInternal.isCeStoragePrepared(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
@@ -25854,7 +25868,8 @@
StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
final int flags;
- if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ if (StorageManager.isUserKeyUnlocked(user.id)
+ && smInternal.isCeStoragePrepared(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 88dd033..5536fc5 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -455,7 +455,7 @@
return state.suspendParams != null && state.suspendParams.containsKey(suspendingPackage);
}
- void addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
+ boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
final PackageUserState existingUserState = modifyUserState(userId);
final PackageUserState.SuspendParams newSuspendParams =
@@ -464,21 +464,27 @@
if (existingUserState.suspendParams == null) {
existingUserState.suspendParams = new ArrayMap<>();
}
- existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
+ final PackageUserState.SuspendParams oldSuspendParams =
+ existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
existingUserState.suspended = true;
onChanged();
+ return !Objects.equals(oldSuspendParams, newSuspendParams);
}
- void removeSuspension(String suspendingPackage, int userId) {
+ boolean removeSuspension(String suspendingPackage, int userId) {
+ boolean wasModified = false;
final PackageUserState existingUserState = modifyUserState(userId);
if (existingUserState.suspendParams != null) {
- existingUserState.suspendParams.remove(suspendingPackage);
+ if (existingUserState.suspendParams.remove(suspendingPackage) != null) {
+ wasModified = true;
+ }
if (existingUserState.suspendParams.size() == 0) {
existingUserState.suspendParams = null;
}
}
existingUserState.suspended = (existingUserState.suspendParams != null);
onChanged();
+ return wasModified;
}
void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d4feb3a..6d8137e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@
import android.os.UserManager.EnforcingUser;
import android.os.UserManager.QuietModeFlag;
import android.os.storage.StorageManager;
+import android.os.storage.StorageManagerInternal;
import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
@@ -4815,6 +4816,10 @@
// Migrate only if build fingerprints mismatch
boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+
+ StorageManagerInternal smInternal = LocalServices.getService(StorageManagerInternal.class);
+ smInternal.markCeStoragePrepared(userId);
+
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 45f5daa..dd2583a0c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -508,7 +508,6 @@
private boolean mKeyguardOccludedChanged;
private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
- volatile boolean mKeyguardOccluded;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
@@ -2399,7 +2398,7 @@
// the keyguard is being hidden. This is okay because starting windows never show
// secret information.
// TODO(b/113840485): Occluded may not only happen on default display
- if (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) {
+ if (displayId == DEFAULT_DISPLAY && isKeyguardOccluded()) {
windowFlags |= FLAG_SHOW_WHEN_LOCKED;
}
}
@@ -3215,7 +3214,7 @@
return;
}
- if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
+ if (!isKeyguardOccluded() && mKeyguardDelegate.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@@ -3266,42 +3265,38 @@
@Override
public void setKeyguardCandidateLw(WindowState win) {
mKeyguardCandidate = win;
- setKeyguardOccludedLw(mKeyguardOccluded, true /* force */);
+ setKeyguardOccludedLw(isKeyguardOccluded(), true /* force */);
}
/**
* Updates the occluded state of the Keyguard.
*
+ * @param isOccluded Whether the Keyguard is occluded by another window.
+ * @param force notify the occluded status to KeyguardService and update flags even though
+ * occlude status doesn't change.
* @return Whether the flags have changed and we have to redo the layout.
*/
private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
- final boolean wasOccluded = mKeyguardOccluded;
- final boolean showing = mKeyguardDelegate.isShowing();
- final boolean changed = wasOccluded != isOccluded || force;
- if (!isOccluded && changed && showing) {
- mKeyguardOccluded = false;
- mKeyguardDelegate.setOccluded(false, true /* animate */);
- if (mKeyguardCandidate != null) {
- if (!mKeyguardDelegate.hasLockscreenWallpaper()) {
- mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
- }
- }
- return true;
- } else if (isOccluded && changed && showing) {
- mKeyguardOccluded = true;
- mKeyguardDelegate.setOccluded(true, false /* animate */);
- if (mKeyguardCandidate != null) {
- mKeyguardCandidate.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
- }
- return true;
- } else if (changed) {
- mKeyguardOccluded = isOccluded;
- mKeyguardDelegate.setOccluded(isOccluded, false /* animate */);
- return false;
- } else {
+ if (isKeyguardOccluded() == isOccluded && !force) {
return false;
}
+
+ final boolean showing = mKeyguardDelegate.isShowing();
+ final boolean animate = showing && !isOccluded;
+ mKeyguardDelegate.setOccluded(isOccluded, animate);
+
+ if (!showing) {
+ return false;
+ }
+ if (mKeyguardCandidate != null) {
+ if (isOccluded) {
+ mKeyguardCandidate.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
+ } else if (!mKeyguardDelegate.hasLockscreenWallpaper()) {
+ mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+ }
+ }
+ return true;
}
/** {@inheritDoc} */
@@ -4596,7 +4591,7 @@
@Override
public boolean isKeyguardShowingAndNotOccluded() {
if (mKeyguardDelegate == null) return false;
- return mKeyguardDelegate.isShowing() && !mKeyguardOccluded;
+ return mKeyguardDelegate.isShowing() && !isKeyguardOccluded();
}
@Override
@@ -4622,7 +4617,7 @@
@Override
public boolean isKeyguardOccluded() {
if (mKeyguardDelegate == null) return false;
- return mKeyguardOccluded;
+ return mKeyguardDelegate.isOccluded();
}
/** {@inheritDoc} */
@@ -5308,7 +5303,7 @@
proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
mDefaultDisplayPolicy.isWindowManagerDrawComplete());
- proto.write(KEYGUARD_OCCLUDED, mKeyguardOccluded);
+ proto.write(KEYGUARD_OCCLUDED, isKeyguardOccluded());
proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged);
proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded);
if (mKeyguardDelegate != null) {
@@ -5393,7 +5388,7 @@
final int key = mDisplayHomeButtonHandlers.keyAt(i);
pw.println(mDisplayHomeButtonHandlers.get(key));
}
- pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(mKeyguardOccluded);
+ pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(isKeyguardOccluded());
pw.print(" mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded);
pw.print(prefix); pw.print("mAllowLockscreenWhenOnDisplays=");
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 0535af5..d190678 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -67,7 +67,7 @@
boolean showing;
boolean showingAndNotOccluded;
boolean inputRestricted;
- boolean occluded;
+ volatile boolean occluded;
boolean secure;
boolean dreaming;
boolean systemIsReady;
@@ -272,6 +272,10 @@
mKeyguardState.occluded = isOccluded;
}
+ public boolean isOccluded() {
+ return mKeyguardState.occluded;
+ }
+
public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
if (mKeyguardService != null) {
mKeyguardService.dismiss(callback, message);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c45b661..168686c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1411,7 +1411,9 @@
this.task = newTask;
if (shouldStartChangeTransition(newParent, oldParent)) {
- initializeChangeTransition(getBounds());
+ // The new parent and old parent may be in different position. Need to offset the
+ // animation surface to keep it in its original position.
+ initializeChangeTransition(getBounds(), newParent.getBounds());
}
super.onParentChanged(newParent, oldParent);
@@ -3790,8 +3792,6 @@
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app token: %s", this);
- commitVisibility(false /* visible */, true /* performLayout */);
-
getDisplayContent().mOpeningApps.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mTaskSnapshotController.onAppRemoved(this);
@@ -3799,8 +3799,6 @@
mTaskSupervisor.mStoppingActivities.remove(this);
waitingToShow = false;
- // TODO(b/169035022): move to a more-appropriate place.
- mAtmService.getTransitionController().collect(this);
// Defer removal of this activity when either a child is animating, or app transition is on
// going. App transition animation might be applied on the parent task not on the activity,
// but the actual frame buffer is associated with the activity, so we have to keep the
@@ -3816,6 +3814,16 @@
delayed = true;
}
+ // Don't commit visibility if it is waiting to animate. It will be set post animation.
+ if (!delayed) {
+ commitVisibility(false /* visible */, true /* performLayout */);
+ } else {
+ setVisibleRequested(false /* visible */);
+ }
+
+ // TODO(b/169035022): move to a more-appropriate place.
+ mAtmService.getTransitionController().collect(this);
+
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Removing app %s delayed=%b animation=%s animating=%b", this, delayed,
getAnimation(),
@@ -4938,11 +4946,10 @@
// dispatchTaskInfoChangedIfNeeded() right after ActivityRecord#setVisibility() can report
// the stale visible state, because the state will be updated after the app transition.
// So tries to report the actual visible state again where the state is changed.
- if (!mTaskSupervisor.inActivityVisibilityUpdate()) {
- final Task task = getOrganizedTask();
- if (task != null) {
- task.dispatchTaskInfoChangedIfNeeded(false /* force */);
- }
+ Task task = getOrganizedTask();
+ while (task != null) {
+ task.dispatchTaskInfoChangedIfNeeded(false /* force */);
+ task = task.getParent().asTask();
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
@@ -4972,7 +4979,7 @@
private void postApplyAnimation(boolean visible) {
final boolean usingShellTransitions =
mAtmService.getTransitionController().getTransitionPlayer() != null;
- final boolean delayed = isAnimating(PARENTS | CHILDREN,
+ final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
| ANIMATION_TYPE_RECENTS);
if (!delayed && !usingShellTransitions) {
@@ -5943,6 +5950,9 @@
}
void startFreezingScreen(int overrideOriginalDisplayRotation) {
+ if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ return;
+ }
ProtoLog.i(WM_DEBUG_ORIENTATION,
"Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
appToken, isVisible(), mFreezingScreen, mVisibleRequested,
@@ -6909,7 +6919,7 @@
@Override
void prepareSurfaces() {
- final boolean show = isVisible() || isAnimating(PARENTS,
+ final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
if (mSurfaceControl != null) {
@@ -8443,9 +8453,7 @@
if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
- if (!mAtmService.getTransitionController().isShellTransitionsEnabled()) {
- startFreezingScreenLocked(globalChanges);
- }
+ startFreezingScreenLocked(globalChanges);
forceNewConfig = false;
// Do not preserve window if it is freezing screen because the original window won't be
// able to update drawn state that causes freeze timeout.
@@ -9269,7 +9277,7 @@
record.getMode(), record.mAdapter.mCapturedLeash, !fillsParent(),
new Rect(), insets,
getPrefixOrderIndex(), record.mAdapter.mPosition, record.mAdapter.mLocalBounds,
- record.mAdapter.mRootTaskBounds, task.getWindowConfiguration(),
+ record.mAdapter.mEndBounds, task.getWindowConfiguration(),
false /*isNotInRecents*/,
record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
record.mStartBounds, task.getTaskInfo(), checkEnterPictureInPictureAppOpsState());
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index bab5a61..980ebf0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2809,7 +2809,7 @@
false /* includingEmbeddedTask */);
final TaskFragment taskFragment = top != null ? top.getTaskFragment() : null;
if (taskFragment != null && taskFragment.isEmbedded()
- && task.effectiveUid == mStartActivity.getUid()) {
+ && canEmbedActivity(taskFragment, mStartActivity, false /* newTask */, task)) {
// Use the embedded TaskFragment of the top activity as the new parent if the
// activity can be embedded.
newParent = top.getTaskFragment();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 0ba77d8..4149980 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -596,11 +596,18 @@
public abstract boolean isBaseOfLockedTask(String packageName);
/**
- * Create an interface to update configuration for an application.
+ * Creates an interface to update configuration for the calling application.
*/
public abstract PackageConfigurationUpdater createPackageConfigurationUpdater();
/**
+ * Creates an interface to update configuration for an arbitrary application specified by it's
+ * packageName and userId.
+ */
+ public abstract PackageConfigurationUpdater createPackageConfigurationUpdater(
+ String packageName, int userId);
+
+ /**
* An interface to update configuration for an application, and will persist override
* configuration for this package.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 681ff90..f278e3e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1732,10 +1732,8 @@
final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
final long origId = Binder.clearCallingIdentity();
try {
- synchronized (mGlobalLock) {
- return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
- safeOptions);
- }
+ return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
+ safeOptions);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -6559,6 +6557,13 @@
}
@Override
+ public PackageConfigurationUpdater createPackageConfigurationUpdater(
+ String packageName , int userId) {
+ return new PackageConfigurationUpdaterImpl(packageName, userId,
+ ActivityTaskManagerService.this);
+ }
+
+ @Override
public boolean hasSystemAlertWindowPermission(int callingUid, int callingPid,
String callingPackage) {
return ActivityTaskManagerService.this.hasSystemAlertWindowPermission(callingUid,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e6aa4fc..145bdfd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1940,6 +1940,7 @@
void removeHistoryRecords(WindowProcessController app) {
removeHistoryRecords(mStoppingActivities, app, "mStoppingActivities");
removeHistoryRecords(mFinishingActivities, app, "mFinishingActivities");
+ removeHistoryRecords(mNoHistoryActivities, app, "mNoHistoryActivities");
}
private void removeHistoryRecords(ArrayList<ActivityRecord> list, WindowProcessController app,
@@ -1977,6 +1978,7 @@
mWaitingActivityLaunched.get(i).dump(pw, prefix + " ");
}
}
+ pw.println(prefix + "mNoHistoryActivities=" + mNoHistoryActivities);
pw.println();
}
@@ -2472,102 +2474,126 @@
}
}
+ /**
+ * Start the given task from the recent tasks. Do not hold WM global lock when calling this
+ * method to avoid potential deadlock or permission deny by UriGrantsManager when resolving
+ * activity (see {@link ActivityStarter.Request#resolveActivity} and
+ * {@link com.android.server.am.ContentProviderHelper#checkContentProviderUriPermission}).
+ *
+ * @return The result code of starter.
+ */
int startActivityFromRecents(int callingPid, int callingUid, int taskId,
SafeActivityOptions options) {
- Task task = null;
+ final Task task;
+ final int taskCallingUid;
final String callingPackage;
final String callingFeatureId;
final Intent intent;
final int userId;
- int activityType = ACTIVITY_TYPE_UNDEFINED;
- int windowingMode = WINDOWING_MODE_UNDEFINED;
final ActivityOptions activityOptions = options != null
? options.getOptions(this)
: null;
boolean moveHomeTaskForward = true;
- if (activityOptions != null) {
- activityType = activityOptions.getLaunchActivityType();
- windowingMode = activityOptions.getLaunchWindowingMode();
- if (activityOptions.freezeRecentTasksReordering()
- && mRecentTasks.isCallerRecents(callingUid)) {
- mRecentTasks.setFreezeTaskListReordering();
+ synchronized (mService.mGlobalLock) {
+ int activityType = ACTIVITY_TYPE_UNDEFINED;
+ if (activityOptions != null) {
+ activityType = activityOptions.getLaunchActivityType();
+ final int windowingMode = activityOptions.getLaunchWindowingMode();
+ if (activityOptions.freezeRecentTasksReordering()
+ && mRecentTasks.isCallerRecents(callingUid)) {
+ mRecentTasks.setFreezeTaskListReordering();
+ }
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || activityOptions.getLaunchRootTask() != null) {
+ // Don't move home activity forward if we are launching into primary split or
+ // there is a launch root set.
+ moveHomeTaskForward = false;
+ }
}
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || activityOptions.getLaunchRootTask() != null) {
- // Don't move home activity forward if we are launching into primary split or there
- // is a launch root set.
- moveHomeTaskForward = false;
- }
- }
- if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
- throw new IllegalArgumentException("startActivityFromRecents: Task "
- + taskId + " can't be launch in the home/recents root task.");
- }
-
- mService.deferWindowLayout();
- try {
- task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
- if (task == null) {
- mWindowManager.executeAppTransition();
- throw new IllegalArgumentException(
- "startActivityFromRecents: Task " + taskId + " not found.");
+ if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
+ throw new IllegalArgumentException("startActivityFromRecents: Task "
+ + taskId + " can't be launch in the home/recents root task.");
}
- if (moveHomeTaskForward) {
- // We always want to return to the home activity instead of the recents activity
- // from whatever is started from the recents activity, so move the home root task
- // forward.
- // TODO (b/115289124): Multi-display supports for recents.
- mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront(
- "startActivityFromRecents");
- }
-
- // If the user must confirm credentials (e.g. when first launching a work app and the
- // Work Challenge is present) let startActivityInPackage handle the intercepting.
- if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId)
- && task.getRootActivity() != null) {
- final ActivityRecord targetActivity = task.getTopNonFinishingActivity();
-
- mRootWindowContainer.startPowerModeLaunchIfNeeded(
- true /* forceSend */, targetActivity);
- final LaunchingState launchingState =
- mActivityMetricsLogger.notifyActivityLaunching(task.intent);
- try {
- mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */,
- task.mTaskId, 0, options);
- // Apply options to prevent pendingOptions be taken when scheduling activity
- // lifecycle transaction to make sure the override pending app transition will
- // be applied immediately.
- targetActivity.applyOptionsAnimation();
- } finally {
- mActivityMetricsLogger.notifyActivityLaunched(launchingState,
- START_TASK_TO_FRONT, false /* newActivityCreated */, targetActivity,
- activityOptions);
+ boolean shouldStartActivity = false;
+ mService.deferWindowLayout();
+ try {
+ task = mRootWindowContainer.anyTaskForId(taskId,
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
+ if (task == null) {
+ mWindowManager.executeAppTransition();
+ throw new IllegalArgumentException(
+ "startActivityFromRecents: Task " + taskId + " not found.");
}
- mService.getActivityStartController().postStartActivityProcessingForLastStarter(
- task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT,
- task.getRootTask());
+ if (moveHomeTaskForward) {
+ // We always want to return to the home activity instead of the recents
+ // activity from whatever is started from the recents activity, so move
+ // the home root task forward.
+ // TODO (b/115289124): Multi-display supports for recents.
+ mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront(
+ "startActivityFromRecents");
+ }
- // As it doesn't go to ActivityStarter.executeRequest() path, we need to resume
- // app switching here also.
- mService.resumeAppSwitches();
+ // If the user must confirm credentials (e.g. when first launching a work
+ // app and the Work Challenge is present) let startActivityInPackage handle
+ // the intercepting.
+ if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId)
+ && task.getRootActivity() != null) {
+ final ActivityRecord targetActivity = task.getTopNonFinishingActivity();
- return ActivityManager.START_TASK_TO_FRONT;
+ mRootWindowContainer.startPowerModeLaunchIfNeeded(
+ true /* forceSend */, targetActivity);
+ final LaunchingState launchingState =
+ mActivityMetricsLogger.notifyActivityLaunching(task.intent);
+ try {
+ mService.moveTaskToFrontLocked(null /* appThread */,
+ null /* callingPackage */, task.mTaskId, 0, options);
+ // Apply options to prevent pendingOptions be taken when scheduling
+ // activity lifecycle transaction to make sure the override pending app
+ // transition will be applied immediately.
+ targetActivity.applyOptionsAnimation();
+ } finally {
+ mActivityMetricsLogger.notifyActivityLaunched(launchingState,
+ START_TASK_TO_FRONT, false /* newActivityCreated */,
+ targetActivity, activityOptions);
+ }
+
+ mService.getActivityStartController().postStartActivityProcessingForLastStarter(
+ task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT,
+ task.getRootTask());
+
+ // As it doesn't go to ActivityStarter.executeRequest() path, we need to resume
+ // app switching here also.
+ mService.resumeAppSwitches();
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
+ // The task is empty or needs to show the confirmation for credential.
+ shouldStartActivity = true;
+ } finally {
+ if (!shouldStartActivity) {
+ mService.continueWindowLayout();
+ }
}
+ taskCallingUid = task.mCallingUid;
callingPackage = task.mCallingPackage;
callingFeatureId = task.mCallingFeatureId;
intent = task.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
userId = task.mUserId;
- return mService.getActivityStartController().startActivityInPackage(task.mCallingUid,
+ }
+ // ActivityStarter will acquire the lock where the places need, so execute the request
+ // outside of the lock.
+ try {
+ return mService.getActivityStartController().startActivityInPackage(taskCallingUid,
callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
null, 0, 0, options, userId, task, "startActivityFromRecents",
false /* validateIncomingUser */, null /* originatingPendingIntent */,
false /* allowBackgroundActivityStart */);
} finally {
- mService.continueWindowLayout();
+ synchronized (mService.mGlobalLock) {
+ mService.continueWindowLayout();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 7f0adca..5589396 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -35,10 +35,10 @@
*/
class AppTaskImpl extends IAppTask.Stub {
private static final String TAG = "AppTaskImpl";
- private ActivityTaskManagerService mService;
+ private final ActivityTaskManagerService mService;
- private int mTaskId;
- private int mCallingUid;
+ private final int mTaskId;
+ private final int mCallingUid;
public AppTaskImpl(ActivityTaskManagerService service, int taskId, int callingUid) {
mService = service;
@@ -113,9 +113,9 @@
return;
}
}
- mService.mTaskSupervisor.startActivityFromRecents(callingPid,
- callingUid, mTaskId, null);
}
+ mService.mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,
+ null /* options */);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index c0b6979..82377b3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -165,8 +165,8 @@
void handleAppTransitionReady() {
mTempTransitionReasons.clear();
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
- || !transitionGoodToGo(mDisplayContent.mChangingContainers,
- mTempTransitionReasons)) {
+ || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)
+ || !transitionGoodToGoForTaskFragments()) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -599,6 +599,13 @@
}
}
+ @Nullable
+ static Task findRootTaskFromContainer(WindowContainer wc) {
+ return wc.asTaskFragment() != null ? wc.asTaskFragment().getRootTask()
+ : wc.asActivityRecord().getRootTask();
+ }
+
+ @Nullable
static ActivityRecord getAppFromContainer(WindowContainer wc) {
return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity()
: wc.asActivityRecord();
@@ -972,72 +979,109 @@
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
-
+ if (mDisplayContent.mAppTransition.isTimeout()) {
+ return true;
+ }
final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent(
Display.DEFAULT_DISPLAY).getRotationAnimation();
- if (!mDisplayContent.mAppTransition.isTimeout()) {
- // Imagine the case where we are changing orientation due to an app transition, but a
- // previous orientation change is still in progress. We won't process the orientation
- // change for our transition because we need to wait for the rotation animation to
- // finish.
- // If we start the app transition at this point, we will interrupt it halfway with a
- // new rotation animation after the old one finally finishes. It's better to defer the
- // app transition.
- if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()
- && mDisplayContent.getDisplayRotation().needsUpdate()) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Delaying app transition for screen rotation animation to finish");
- return false;
- }
- for (int i = 0; i < apps.size(); i++) {
- WindowContainer wc = apps.valueAt(i);
- final ActivityRecord activity = getAppFromContainer(wc);
- if (activity == null) {
- continue;
- }
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
- + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
- activity, activity.allDrawn, activity.startingDisplayed,
- activity.startingMoved, activity.isRelaunching(),
- activity.mStartingWindow);
-
-
- final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
- if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
- return false;
- }
- if (allDrawn) {
- outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN);
- } else {
- outReasons.put(activity,
- activity.mStartingData instanceof SplashScreenStartingData
- ? APP_TRANSITION_SPLASH_SCREEN
- : APP_TRANSITION_SNAPSHOT);
- }
- }
-
- // We also need to wait for the specs to be fetched, if needed.
- if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true");
- return false;
- }
-
- if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s",
- mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
- return false;
- }
-
- // If the wallpaper is visible, we need to check it's ready too.
- boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
- mWallpaperControllerLocked.wallpaperTransitionReady();
- if (wallpaperReady) {
- return true;
- }
+ // Imagine the case where we are changing orientation due to an app transition, but a
+ // previous orientation change is still in progress. We won't process the orientation
+ // change for our transition because we need to wait for the rotation animation to
+ // finish.
+ // If we start the app transition at this point, we will interrupt it halfway with a
+ // new rotation animation after the old one finally finishes. It's better to defer the
+ // app transition.
+ if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()
+ && mDisplayContent.getDisplayRotation().needsUpdate()) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Delaying app transition for screen rotation animation to finish");
return false;
}
+ for (int i = 0; i < apps.size(); i++) {
+ WindowContainer wc = apps.valueAt(i);
+ final ActivityRecord activity = getAppFromContainer(wc);
+ if (activity == null) {
+ continue;
+ }
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
+ activity, activity.allDrawn, activity.startingDisplayed,
+ activity.startingMoved, activity.isRelaunching(),
+ activity.mStartingWindow);
+
+ final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
+ if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
+ return false;
+ }
+ if (allDrawn) {
+ outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN);
+ } else {
+ outReasons.put(activity,
+ activity.mStartingData instanceof SplashScreenStartingData
+ ? APP_TRANSITION_SPLASH_SCREEN
+ : APP_TRANSITION_SNAPSHOT);
+ }
+ }
+
+ // We also need to wait for the specs to be fetched, if needed.
+ if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true");
+ return false;
+ }
+
+ if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s",
+ mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
+ return false;
+ }
+
+ // If the wallpaper is visible, we need to check it's ready too.
+ return !mWallpaperControllerLocked.isWallpaperVisible()
+ || mWallpaperControllerLocked.wallpaperTransitionReady();
+ }
+
+ private boolean transitionGoodToGoForTaskFragments() {
+ if (mDisplayContent.mAppTransition.isTimeout()) {
+ return true;
+ }
+
+ // Check all Tasks in this transition. This is needed because new TaskFragment created for
+ // launching activity may not be in the tracking lists, but we still want to wait for the
+ // activity launch to start the transition.
+ final ArraySet<Task> rootTasks = new ArraySet<>();
+ for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
+ rootTasks.add(mDisplayContent.mOpeningApps.valueAt(i).getRootTask());
+ }
+ for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
+ rootTasks.add(mDisplayContent.mClosingApps.valueAt(i).getRootTask());
+ }
+ for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
+ rootTasks.add(
+ findRootTaskFromContainer(mDisplayContent.mChangingContainers.valueAt(i)));
+ }
+
+ // Organized TaskFragment can be empty for two situations:
+ // 1. New created and is waiting for Activity launch. In this case, we want to wait for
+ // the Activity launch to trigger the transition.
+ // 2. Last Activity is just removed. In this case, we want to wait for organizer to
+ // remove the TaskFragment because it may also want to change other TaskFragments in
+ // the same transition.
+ for (int i = rootTasks.size() - 1; i >= 0; i--) {
+ final Task rootTask = rootTasks.valueAt(i);
+ final boolean notReady = rootTask.forAllLeafTaskFragments(taskFragment -> {
+ if (!taskFragment.isReadyToTransit()) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Organized TaskFragment is not ready= %s",
+ taskFragment);
+ return true;
+ }
+ return false;
+ });
+ if (notReady) {
+ return false;
+ }
+ }
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 63f6387..f800f0e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -76,6 +76,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
@@ -1389,11 +1390,16 @@
final Configuration currentDisplayConfig = getConfiguration();
mTmpConfiguration.setTo(currentDisplayConfig);
computeScreenConfiguration(mTmpConfiguration);
- configChanged |= currentDisplayConfig.diff(mTmpConfiguration) != 0;
+ final int changes = currentDisplayConfig.diff(mTmpConfiguration);
+ configChanged |= changes != 0;
if (configChanged) {
mWaitingForConfig = true;
- mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
+ if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ requestChangeTransitionIfNeeded(changes);
+ } else {
+ mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
+ }
sendNewConfiguration();
}
@@ -3165,6 +3171,24 @@
return mScreenRotationAnimation;
}
+ /**
+ * Requests to start a transition for the display configuration change. The given changes must
+ * be non-zero. This method is no-op if the display has been collected.
+ */
+ void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes) {
+ final TransitionController controller = mAtmService.getTransitionController();
+ if (controller.isCollecting()) {
+ if (!controller.isCollecting(this)) {
+ controller.collect(this);
+ }
+ return;
+ }
+ final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, this);
+ if (t != null) {
+ t.setKnownConfigChanges(this, changes);
+ }
+ }
+
/** If the display is in transition, there should be a screenshot covering it. */
@Override
boolean inTransition() {
@@ -5724,6 +5748,9 @@
}
mWmService.mDisplayNotificationController.dispatchDisplayChanged(
this, getConfiguration());
+ if (isReady() && mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ requestChangeTransitionIfNeeded(changes);
+ }
}
return changes;
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index c9db14d..225a6ea 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -22,7 +22,6 @@
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
@@ -443,7 +442,9 @@
return false;
}
- if (mDisplayContent.mFixedRotationTransitionListener
+ final RecentsAnimationController recentsAnimController =
+ mService.getRecentsAnimationController();
+ if (recentsAnimController != null && mDisplayContent.mFixedRotationTransitionListener
.isTopFixedOrientationRecentsAnimating()
// If screen is off or the device is going to sleep, then still allow to update.
&& mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
@@ -451,6 +452,7 @@
// In order to ignore its requested orientation to avoid a sensor led rotation (e.g
// user rotating the device while the recents animation is running), we ignore
// rotation update while the animation is running.
+ recentsAnimController.setCheckRotationAfterCleanup();
return false;
}
}
@@ -492,12 +494,6 @@
recentsAnimationController.cancelAnimationForDisplayChange();
}
- final Transition t = (useShellTransitions
- && !mService.mAtmService.getTransitionController().isCollecting())
- ? mService.mAtmService.getTransitionController().createTransition(TRANSIT_CHANGE)
- : null;
- mService.mAtmService.getTransitionController().collect(mDisplayContent);
-
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Display id=%d rotation changed to %d from %d, lastOrientation=%d",
displayId, rotation, oldRotation, lastOrientation);
@@ -511,11 +507,10 @@
mDisplayContent.setLayoutNeeded();
if (useShellTransitions) {
- if (t != null) {
- // This created its own transition, so send a start request.
- mService.mAtmService.getTransitionController().requestStartTransition(
- t, null /* trigger */, null /* remote */);
- } else {
+ final boolean wasInTransition = mDisplayContent.inTransition();
+ mDisplayContent.requestChangeTransitionIfNeeded(
+ ActivityInfo.CONFIG_WINDOW_CONFIGURATION);
+ if (wasInTransition) {
// Use remote-rotation infra since the transition has already been requested
// TODO(shell-transitions): Remove this once lifecycle management can cover all
// rotation cases.
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index d12d07a..cc6a880 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.content.ClipData;
+import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -32,6 +33,7 @@
import android.view.IWindow;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
@@ -43,7 +45,8 @@
*/
class DragDropController {
private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
- private static final long DRAG_TIMEOUT_MS = 5000;
+ static final long DRAG_TIMEOUT_MS = 5000;
+ private static final int A11Y_DRAG_TIMEOUT_DEFAULT_MS = 60000;
// Messages for Handler.
static final int MSG_DRAG_END_TIMEOUT = 0;
@@ -151,36 +154,48 @@
mDragState.mOriginalAlpha = alpha;
mDragState.mToken = dragToken;
mDragState.mDisplayContent = displayContent;
-
- final Display display = displayContent.getDisplay();
- if (!mCallback.get().registerInputChannel(
- mDragState, display, mService.mInputManager,
- callingWin.mInputChannel)) {
- Slog.e(TAG_WM, "Unable to transfer touch focus");
- return null;
- }
-
- final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
mDragState.mData = data;
- mDragState.broadcastDragStartedLocked(touchX, touchY);
- mDragState.overridePointerIconLocked(touchSource);
- // remember the thumb offsets for later
- mDragState.mThumbOffsetX = thumbCenterX;
- mDragState.mThumbOffsetY = thumbCenterY;
- // Make the surface visible at the proper location
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
+ if ((flags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) == 0) {
+ final Display display = displayContent.getDisplay();
+ if (!mCallback.get().registerInputChannel(
+ mDragState, display, mService.mInputManager,
+ callingWin.mInputChannel)) {
+ Slog.e(TAG_WM, "Unable to transfer touch focus");
+ return null;
+ }
- final SurfaceControl.Transaction transaction = mDragState.mTransaction;
- transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
- transaction.setPosition(
- surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY);
- transaction.show(surfaceControl);
- displayContent.reparentToOverlay(transaction, surfaceControl);
- callingWin.scheduleAnimation();
+ final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
+ mDragState.broadcastDragStartedLocked(touchX, touchY);
+ mDragState.overridePointerIconLocked(touchSource);
+ // remember the thumb offsets for later
+ mDragState.mThumbOffsetX = thumbCenterX;
+ mDragState.mThumbOffsetY = thumbCenterY;
- if (SHOW_LIGHT_TRANSACTIONS) {
- Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
+ // Make the surface visible at the proper location
+ if (SHOW_LIGHT_TRANSACTIONS) {
+ Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
+ }
+
+ final SurfaceControl.Transaction transaction = mDragState.mTransaction;
+ transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
+ transaction.setPosition(
+ surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY);
+ transaction.show(surfaceControl);
+ displayContent.reparentToOverlay(transaction, surfaceControl);
+ callingWin.scheduleAnimation();
+ if (SHOW_LIGHT_TRANSACTIONS) {
+ Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
+ }
+ } else {
+ // Skip surface logic for a drag triggered by an AccessibilityAction
+ mDragState.broadcastDragStartedLocked(touchX, touchY);
+
+ // Timeout for the user to drop the content
+ sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, callingWin.mClient.asBinder(),
+ getAccessibilityManager().getRecommendedTimeoutMillis(
+ A11Y_DRAG_TIMEOUT_DEFAULT_MS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS));
}
} finally {
if (surface != null) {
@@ -309,10 +324,10 @@
/**
* Sends a timeout message to the Handler managed by DragDropController.
*/
- void sendTimeoutMessage(int what, Object arg) {
+ void sendTimeoutMessage(int what, Object arg, long timeoutMs) {
mHandler.removeMessages(what, arg);
final Message msg = mHandler.obtainMessage(what, arg);
- mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
+ mHandler.sendMessageDelayed(msg, timeoutMs);
}
/**
@@ -332,6 +347,30 @@
}
}
+ boolean dropForAccessibility(IWindow window, float x, float y) {
+ synchronized (mService.mGlobalLock) {
+ final boolean isA11yEnabled = getAccessibilityManager().isEnabled();
+ if (!dragDropActiveLocked()) {
+ return false;
+ }
+ if (mDragState.isAccessibilityDragDrop() && isA11yEnabled) {
+ final WindowState winState = mService.windowForClientLocked(
+ null, window, false);
+ if (!mDragState.isWindowNotified(winState)) {
+ return false;
+ }
+ IBinder token = winState.mInputChannelToken;
+ return mDragState.reportDropWindowLock(token, x, y);
+ }
+ return false;
+ }
+ }
+
+ AccessibilityManager getAccessibilityManager() {
+ return (AccessibilityManager) mService.mContext.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ }
+
private class DragHandler extends Handler {
/**
* Lock for window manager.
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index aa257f8..4fc123d 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -253,7 +253,7 @@
mTransaction.reparent(mSurfaceControl, null).apply();
} else {
mDragDropController.sendTimeoutMessage(MSG_REMOVE_DRAG_SURFACE_TIMEOUT,
- mSurfaceControl);
+ mSurfaceControl, DragDropController.DRAG_TIMEOUT_MS);
}
mSurfaceControl = null;
}
@@ -276,9 +276,9 @@
* Notify the drop target and tells it about the data. If the drop event is not sent to the
* target, invokes {@code endDragLocked} immediately.
*/
- void reportDropWindowLock(IBinder token, float x, float y) {
+ boolean reportDropWindowLock(IBinder token, float x, float y) {
if (mAnimator != null) {
- return;
+ return false;
}
final WindowState touchedWin = mService.mInputToWindowMap.get(token);
@@ -288,7 +288,7 @@
mDragResult = false;
endDragLocked();
if (DEBUG_DRAG) Slog.d(TAG_WM, "Drop outside a valid window " + touchedWin);
- return;
+ return false;
}
if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin);
@@ -322,16 +322,19 @@
touchedWin.mClient.dispatchDragEvent(event);
// 5 second timeout for this window to respond to the drop
- mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, clientToken);
+ mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, clientToken,
+ DragDropController.DRAG_TIMEOUT_MS);
} catch (RemoteException e) {
Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin);
endDragLocked();
+ return false;
} finally {
if (myPid != touchedWin.mSession.mPid) {
event.recycle();
}
}
mToken = clientToken;
+ return true;
}
class InputInterceptor {
@@ -553,7 +556,7 @@
}
}
- private boolean isWindowNotified(WindowState newWin) {
+ boolean isWindowNotified(WindowState newWin) {
for (WindowState ws : mNotifiedWindows) {
if (ws == newWin) {
return true;
@@ -567,8 +570,10 @@
return;
}
if (!mDragResult) {
- mAnimator = createReturnAnimationLocked();
- return; // Will call closeLocked() when the animation is done.
+ if (!isAccessibilityDragDrop()) {
+ mAnimator = createReturnAnimationLocked();
+ return; // Will call closeLocked() when the animation is done.
+ }
}
closeLocked();
}
@@ -577,7 +582,7 @@
if (mAnimator != null) {
return;
}
- if (!mDragInProgress || skipAnimation) {
+ if (!mDragInProgress || skipAnimation || isAccessibilityDragDrop()) {
// mDragInProgress is false if an app invokes Session#cancelDragAndDrop before
// Session#performDrag. Reset the drag state without playing the cancel animation
// because H.DRAG_START_TIMEOUT may be sent to WindowManagerService, which will cause
@@ -722,4 +727,8 @@
mDragDropController.sendHandlerMessage(MSG_ANIMATION_END, null);
}
}
+
+ boolean isAccessibilityDragDrop() {
+ return (mFlags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index fed4f62..6560d15 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -103,12 +103,12 @@
ArrayList<TaskFragment> adjacentTaskFragments = null;
for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) {
final WindowContainer child = mTaskFragment.mChildren.get(i);
- if (child.asTaskFragment() != null) {
- final TaskFragment childTaskFragment = child.asTaskFragment();
+ final TaskFragment childTaskFragment = child.asTaskFragment();
+ if (childTaskFragment != null && childTaskFragment.topRunningActivity() != null) {
childTaskFragment.updateActivityVisibilities(starting, configChanges,
preserveWindows, notifyClients);
- mBehindFullyOccludedContainer |= childTaskFragment.getBounds().equals(
- mTaskFragment.getBounds());
+ mBehindFullyOccludedContainer |=
+ childTaskFragment.getBounds().equals(mTaskFragment.getBounds());
if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) {
mAboveTop = false;
}
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 76a098d..72fbfcc 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -174,7 +174,7 @@
* Whether corners of letterboxed activities are rounded.
*/
boolean isLetterboxActivityCornersRounded() {
- return getLetterboxActivityCornersRadius() > 0;
+ return getLetterboxActivityCornersRadius() != 0;
}
/**
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 0d99bac..cf2afc9 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -38,6 +38,9 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Slog;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
@@ -295,31 +298,66 @@
}
private void updateRoundedCorners(WindowState mainWindow) {
- int cornersRadius =
- // Don't round corners if letterboxed only for display cutout.
- shouldShowLetterboxUi(mainWindow)
- && !mainWindow.isLetterboxedForDisplayCutout()
- ? Math.max(0, mLetterboxConfiguration.getLetterboxActivityCornersRadius())
- : 0;
- setCornersRadius(mainWindow, cornersRadius);
- }
-
- private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
if (windowSurface != null && windowSurface.isValid()) {
Transaction transaction = mActivityRecord.getSyncTransaction();
- transaction.setCornerRadius(windowSurface, cornersRadius);
+
+ if (!isLetterboxedNotForDisplayCutout(mainWindow)
+ || !mLetterboxConfiguration.isLetterboxActivityCornersRounded()) {
+ transaction
+ .setWindowCrop(windowSurface, null)
+ .setCornerRadius(windowSurface, 0);
+ return;
+ }
+
+ final InsetsState insetsState = mainWindow.getInsetsState();
+ final InsetsSource taskbarInsetsSource =
+ insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+
+ Rect cropBounds = new Rect(mActivityRecord.getBounds());
+ // Activity bounds are in screen coordinates while (0,0) for activity's surface control
+ // is at the top left corner of an app window so offsetting bounds accordingly.
+ cropBounds.offsetTo(0, 0);
+ // Rounded cornerners should be displayed above the taskbar.
+ cropBounds.bottom = Math.min(cropBounds.bottom, taskbarInsetsSource.getFrame().top);
+ transaction
+ .setWindowCrop(windowSurface, cropBounds)
+ .setCornerRadius(windowSurface, getRoundedCorners(insetsState));
}
}
+ // Returns rounded corners radius based on override in
+ // R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+ // Device corners can be different on the right and left sides but we use the same radius
+ // for all corners for consistency and pick a minimal bottom one for consistency with a
+ // taskbar rounded corners.
+ private int getRoundedCorners(InsetsState insetsState) {
+ if (mLetterboxConfiguration.getLetterboxActivityCornersRadius() >= 0) {
+ return mLetterboxConfiguration.getLetterboxActivityCornersRadius();
+ }
+ return Math.min(
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
+ }
+
+ private int getInsetsStateCornerRadius(
+ InsetsState insetsState, @RoundedCorner.Position int position) {
+ RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
+ return corner == null ? 0 : corner.getRadius();
+ }
+
+ private boolean isLetterboxedNotForDisplayCutout(WindowState mainWindow) {
+ return shouldShowLetterboxUi(mainWindow)
+ && !mainWindow.isLetterboxedForDisplayCutout();
+ }
+
private void updateWallpaperForLetterbox(WindowState mainWindow) {
@LetterboxBackgroundType int letterboxBackgroundType =
mLetterboxConfiguration.getLetterboxBackgroundType();
boolean wallpaperShouldBeShown =
letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
- && shouldShowLetterboxUi(mainWindow)
// Don't use wallpaper as a background if letterboxed for display cutout.
- && !mainWindow.isLetterboxedForDisplayCutout()
+ && isLetterboxedNotForDisplayCutout(mainWindow)
// Check that dark scrim alpha or blur radius are provided
&& (getLetterboxWallpaperBlurRadius() > 0
|| getLetterboxWallpaperDarkScrimAlpha() > 0)
@@ -375,6 +413,8 @@
pw.println(prefix + " letterboxBackgroundType="
+ letterboxBackgroundTypeToString(
mLetterboxConfiguration.getLetterboxBackgroundType()));
+ pw.println(prefix + " letterboxCornerRadius="
+ + getRoundedCorners(mainWin.getInsetsState()));
if (mLetterboxConfiguration.getLetterboxBackgroundType()
== LETTERBOX_BACKGROUND_WALLPAPER) {
pw.println(prefix + " isLetterboxWallpaperBlurSupported="
diff --git a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
index 96025054..8bbcf1f 100644
--- a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
+++ b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
@@ -16,26 +16,40 @@
package com.android.server.wm;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.LocaleList;
+import android.util.ArraySet;
import android.util.Slog;
+import java.util.Optional;
+
/**
* An implementation of {@link ActivityTaskManagerInternal.PackageConfigurationUpdater}.
*/
final class PackageConfigurationUpdaterImpl implements
ActivityTaskManagerInternal.PackageConfigurationUpdater {
private static final String TAG = "PackageConfigurationUpdaterImpl";
- private final int mPid;
+ private final Optional<Integer> mPid;
private Integer mNightMode;
private LocaleList mLocales;
+ private String mPackageName;
+ private int mUserId;
private ActivityTaskManagerService mAtm;
PackageConfigurationUpdaterImpl(int pid, ActivityTaskManagerService atm) {
- mPid = pid;
+ mPid = Optional.of(pid);
mAtm = atm;
}
+ PackageConfigurationUpdaterImpl(String packageName, int userId,
+ ActivityTaskManagerService atm) {
+ mPackageName = packageName;
+ mUserId = userId;
+ mAtm = atm;
+ mPid = Optional.empty();
+ }
+
@Override
public ActivityTaskManagerInternal.PackageConfigurationUpdater setNightMode(int nightMode) {
synchronized (this) {
@@ -59,16 +73,29 @@
synchronized (mAtm.mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final WindowProcessController wpc = mAtm.mProcessMap.getProcess(mPid);
- if (wpc == null) {
- Slog.w(TAG, "Override application configuration: cannot find pid " + mPid);
- return;
+ final int uid;
+ if (mPid.isPresent()) {
+ WindowProcessController wpc = mAtm.mProcessMap.getProcess(mPid.get());
+ if (wpc == null) {
+ Slog.w(TAG, "commit: Override application configuration failed: "
+ + "cannot find pid " + mPid);
+ return;
+ }
+ uid = wpc.mUid;
+ mUserId = wpc.mUserId;
+ mPackageName = wpc.mInfo.packageName;
+ } else {
+ uid = mAtm.getPackageManagerInternalLocked().getPackageUid(mPackageName,
+ /* flags = */ PackageManager.MATCH_ALL, mUserId);
+ if (uid < 0) {
+ Slog.w(TAG, "commit: update of application configuration failed: "
+ + "userId or packageName not valid " + mUserId);
+ return;
+ }
}
- LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
- mLocales, mAtm.getGlobalConfiguration().getLocales());
- wpc.applyAppSpecificConfig(mNightMode, localesOverride);
- wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
- mAtm.mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this);
+ updateConfig(uid, mPackageName);
+ mAtm.mPackageConfigPersister.updateFromImpl(mPackageName, mUserId, this);
+
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -76,6 +103,19 @@
}
}
+ private void updateConfig(int uid, String packageName) {
+ final ArraySet<WindowProcessController> processes = mAtm.mProcessMap.getProcesses(uid);
+ if (processes == null) return;
+ for (int i = processes.size() - 1; i >= 0; i--) {
+ final WindowProcessController wpc = processes.valueAt(i);
+ if (!wpc.mInfo.packageName.equals(packageName)) continue;
+ LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
+ mLocales, mAtm.getGlobalConfiguration().getLocales());
+ wpc.applyAppSpecificConfig(mNightMode, localesOverride);
+ wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
+ }
+ }
+
Integer getNightMode() {
return mNightMode;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index ba85c98..03ff06c 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -123,6 +123,7 @@
private final int mDisplayId;
private boolean mWillFinishToHome = false;
private final Runnable mFailsafeRunnable = this::onFailsafe;
+ private Runnable mCheckRotationAfterCleanup;
// The recents component app token that is shown behind the visibile tasks
private ActivityRecord mTargetActivityRecord;
@@ -921,6 +922,24 @@
}
/**
+ * If the display rotation change is ignored while recents animation is running, make sure that
+ * the pending rotation change will be applied after the animation finishes.
+ */
+ void setCheckRotationAfterCleanup() {
+ if (mCheckRotationAfterCleanup != null) return;
+ mCheckRotationAfterCleanup = () -> {
+ synchronized (mService.mGlobalLock) {
+ if (mDisplayContent.getDisplayRotation()
+ .updateRotationAndSendNewConfigIfChanged()) {
+ if (mTargetActivityRecord != null) {
+ mTargetActivityRecord.finishFixedRotationTransform();
+ }
+ }
+ }
+ };
+ }
+
+ /**
* @return Whether we should defer the cancel from a root task order change until the next app
* transition.
*/
@@ -1007,6 +1026,10 @@
if (mStatusBar != null) {
mStatusBar.onRecentsAnimationStateChanged(false /* running */);
}
+ if (mCheckRotationAfterCleanup != null) {
+ mService.mH.post(mCheckRotationAfterCleanup);
+ mCheckRotationAfterCleanup = null;
+ }
}
void scheduleFailsafe() {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index b90f937..16a45fe 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -65,7 +65,6 @@
new ArrayList<>();
@VisibleForTesting
final ArrayList<NonAppWindowAnimationAdapter> mPendingNonAppAnimations = new ArrayList<>();
- private final Rect mTmpRect = new Rect();
private final Handler mHandler;
private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
@@ -85,18 +84,18 @@
* Creates an animation record for each individual {@link WindowContainer}.
*
* @param windowContainer The windows to animate.
- * @param position The position app bounds, in screen coordinates.
+ * @param position The position app bounds relative to its parent.
* @param localBounds The bounds of the app relative to its parent.
- * @param stackBounds The stack bounds of the app relative to position.
- * @param startBounds The stack bounds before the transition, in screen coordinates
+ * @param endBounds The end bounds after the transition, in screen coordinates.
+ * @param startBounds The start bounds before the transition, in screen coordinates.
* @return The record representing animation(s) to run on the app.
*/
RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
- Point position, Rect localBounds, Rect stackBounds, Rect startBounds) {
+ Point position, Rect localBounds, Rect endBounds, Rect startBounds) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
windowContainer);
final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
- localBounds, stackBounds, startBounds);
+ localBounds, endBounds, startBounds);
mPendingAnimations.add(adapters);
return adapters;
}
@@ -405,16 +404,17 @@
mStartBounds = new Rect(startBounds);
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
mStartBounds);
- mTmpRect.set(startBounds);
- mTmpRect.offsetTo(0, 0);
if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
- mThumbnailAdapter =
- new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
- mTmpRect, new Rect());
+ final Rect thumbnailLocalBounds = new Rect(startBounds);
+ thumbnailLocalBounds.offsetTo(0, 0);
+ // Snapshot is located at (0,0) of the animation leash. It doesn't have size
+ // change, so the startBounds is its end bounds, and no start bounds for it.
+ mThumbnailAdapter = new RemoteAnimationAdapterWrapper(this, new Point(0, 0),
+ thumbnailLocalBounds, startBounds, new Rect());
}
} else {
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
- new Rect(endPos.x, endPos.y, endBounds.right, endBounds.bottom));
+ new Rect());
mStartBounds = null;
}
}
@@ -458,15 +458,15 @@
private @AnimationType int mAnimationType;
final Point mPosition = new Point();
final Rect mLocalBounds;
- final Rect mRootTaskBounds = new Rect();
+ final Rect mEndBounds = new Rect();
final Rect mStartBounds = new Rect();
RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
- Rect localBounds, Rect rootTaskBounds, Rect startBounds) {
+ Rect localBounds, Rect endBounds, Rect startBounds) {
mRecord = record;
mPosition.set(position.x, position.y);
mLocalBounds = localBounds;
- mRootTaskBounds.set(rootTaskBounds);
+ mEndBounds.set(endBounds);
mStartBounds.set(startBounds);
}
@@ -480,12 +480,17 @@
@AnimationType int type, OnAnimationFinishedCallback finishCallback) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
- // Restore position and stack crop until client has a chance to modify it.
if (mStartBounds.isEmpty()) {
- t.setPosition(animationLeash, 0, 0);
- t.setWindowCrop(animationLeash, -1, -1);
+ // Restore position and stack crop until client has a chance to modify it.
+ t.setPosition(animationLeash, mPosition.x, mPosition.y);
+ t.setWindowCrop(animationLeash, mEndBounds.width(), mEndBounds.height());
} else {
- t.setPosition(animationLeash, mStartBounds.left, mStartBounds.top);
+ // Offset the change animation leash to the relative start position in parent.
+ // (mPosition) is the relative end position in parent container.
+ // (mStartBounds - mEndBounds) is the position difference between start and end.
+ // (mPosition + mStartBounds - mEndBounds) will be the relative start position.
+ t.setPosition(animationLeash, mPosition.x + mStartBounds.left - mEndBounds.left,
+ mPosition.y + mStartBounds.top - mEndBounds.top);
t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
}
mCapturedLeash = animationLeash;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0b56777..8c056b2 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -299,6 +299,17 @@
}
}
+
+ @Override
+ public boolean dropForAccessibility(IWindow window, int x, int y) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mDragDropController.dropForAccessibility(window, x, y);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Validates the given drag data.
*/
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 3c6c23b..c7bf8ec 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -59,11 +59,30 @@
@VisibleForTesting
final Animatable mAnimatable;
private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
+
+ /**
+ * Static callback to run on all animations started through this SurfaceAnimator
+ * when an animation on a Surface is finished or cancelled without restart.
+ */
@VisibleForTesting
@Nullable
final OnAnimationFinishedCallback mStaticAnimationFinishedCallback;
+
+ /**
+ * Callback unique to each animation (i.e. AnimationAdapter). To be run when an animation on a
+ * Surface is finished or cancelled without restart.
+ */
@Nullable
- private OnAnimationFinishedCallback mAnimationFinishedCallback;
+ private OnAnimationFinishedCallback mSurfaceAnimationFinishedCallback;
+
+ /**
+ * The callback is triggered after the SurfaceAnimator sends a cancel call to the underlying
+ * AnimationAdapter.
+ * NOTE: Must be called wherever we call onAnimationCancelled on mAnimation.
+ */
+ @Nullable
+ private Runnable mAnimationCancelledCallback;
+
private boolean mAnimationStartDelayed;
/**
@@ -100,7 +119,7 @@
return;
}
final OnAnimationFinishedCallback animationFinishCallback =
- mAnimationFinishedCallback;
+ mSurfaceAnimationFinishedCallback;
reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
if (staticAnimationFinishedCallback != null) {
staticAnimationFinishedCallback.onAnimationFinished(type, anim);
@@ -130,15 +149,19 @@
* This is important as it will start with the leash hidden or visible before
* handing it to the component that is responsible to run the animation.
* @param animationFinishedCallback The callback being triggered when the animation finishes.
+ * @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
+ * cancel call to the underlying AnimationAdapter.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
+ @Nullable Runnable animationCancelledCallback,
@Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
- mAnimationFinishedCallback = animationFinishedCallback;
+ mSurfaceAnimationFinishedCallback = animationFinishedCallback;
+ mAnimationCancelledCallback = animationCancelledCallback;
final SurfaceControl surface = mAnimatable.getSurfaceControl();
if (surface == null) {
Slog.w(TAG, "Unable to start animation, surface is null or no children.");
@@ -161,14 +184,9 @@
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
- @AnimationType int type,
- @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
- startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* freezer */);
- }
-
- void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
- startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
+ startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */,
+ null /* animationCancelledCallback */, null /* freezer */);
}
/**
@@ -278,7 +296,8 @@
mLeash = from.mLeash;
mAnimation = from.mAnimation;
mAnimationType = from.mAnimationType;
- mAnimationFinishedCallback = from.mAnimationFinishedCallback;
+ mSurfaceAnimationFinishedCallback = from.mSurfaceAnimationFinishedCallback;
+ mAnimationCancelledCallback = from.mAnimationCancelledCallback;
// Cancel source animation, but don't let animation runner cancel the animation.
from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */);
@@ -306,11 +325,16 @@
final SurfaceControl leash = mLeash;
final AnimationAdapter animation = mAnimation;
final @AnimationType int animationType = mAnimationType;
- final OnAnimationFinishedCallback animationFinishedCallback = mAnimationFinishedCallback;
+ final OnAnimationFinishedCallback animationFinishedCallback =
+ mSurfaceAnimationFinishedCallback;
+ final Runnable animationCancelledCallback = mAnimationCancelledCallback;
reset(t, false);
if (animation != null) {
if (!mAnimationStartDelayed && forwardCancel) {
animation.onAnimationCancelled(leash);
+ if (animationCancelledCallback != null) {
+ animationCancelledCallback.run();
+ }
}
if (!restarting) {
if (mStaticAnimationFinishedCallback != null) {
@@ -335,7 +359,7 @@
private void reset(Transaction t, boolean destroyLeash) {
mService.mAnimationTransferMap.remove(mAnimation);
mAnimation = null;
- mAnimationFinishedCallback = null;
+ mSurfaceAnimationFinishedCallback = null;
mAnimationType = ANIMATION_TYPE_NONE;
if (mLeash == null) {
return;
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index fce2f8d..9c4f6f5 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.view.Surface;
@@ -70,13 +71,14 @@
* above the target surface) and then taking a snapshot and placing it over the target surface.
*
* @param startBounds The original bounds (on screen) of the surface we are snapshotting.
+ * @param relativePosition The related position of the snapshot surface to its parent.
*/
- void freeze(SurfaceControl.Transaction t, Rect startBounds) {
+ void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition) {
mFreezeBounds.set(startBounds);
mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
t, ANIMATION_TYPE_SCREEN_ROTATION, startBounds.width(), startBounds.height(),
- startBounds.left, startBounds.top, false /* hidden */,
+ relativePosition.x, relativePosition.y, false /* hidden */,
mWmService.mTransactionFactory);
mAnimatable.onAnimationLeashCreated(t, mLeash);
@@ -123,6 +125,18 @@
}
}
+ void setLayer(SurfaceControl.Transaction t, int layer) {
+ if (mLeash != null) {
+ t.setLayer(mLeash, layer);
+ }
+ }
+
+ void setRelativeLayer(SurfaceControl.Transaction t, SurfaceControl relativeTo, int layer) {
+ if (mLeash != null) {
+ t.setRelativeLayer(mLeash, relativeTo, layer);
+ }
+ }
+
boolean hasLeash() {
return mLeash != null;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a3626d2..c5362d3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2347,11 +2347,6 @@
return getRootTask().mTaskId;
}
- @Nullable
- Task getRootTask() {
- return getRootTaskFragment().asTask();
- }
-
/** @return the first organized task. */
@Nullable
Task getOrganizedTask() {
@@ -4814,10 +4809,6 @@
notifyClients);
}, true /* traverseTopToBottom */);
- // Notify WM shell that task visibilities may have changed
- forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false),
- true /* traverseTopToBottom */);
-
if (mTranslucentActivityWaiting != null &&
mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
// Nothing is getting drawn or everything was already visible, don't wait for
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4a1a922..66f2dbc 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -42,6 +42,9 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static java.lang.Integer.MIN_VALUE;
+
+import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
@@ -80,6 +83,22 @@
DisplayContent mDisplayContent;
/**
+ * A color layer that serves as a solid color background to certain animations.
+ */
+ private SurfaceControl mColorBackgroundLayer;
+
+ /**
+ * This counter is used to make sure we don't prematurely clear the background color in the
+ * case that background color animations are interleaved.
+ * NOTE: The last set color will remain until the counter is reset to 0, which means that an
+ * animation background color may sometime remain after the animation has finished through an
+ * animation with a different background color if an animation starts after and ends before
+ * another where both set different background colors. However, this is not a concern as
+ * currently all task animation backgrounds are the same color.
+ */
+ private int mColorLayerCounter = 0;
+
+ /**
* A control placed at the appropriate level for transitions to occur.
*/
private SurfaceControl mAppAnimationLayer;
@@ -959,6 +978,11 @@
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
if (getParent() != null) {
super.onParentChanged(newParent, oldParent, () -> {
+ mColorBackgroundLayer = makeChildSurface(null)
+ .setColorLayer()
+ .setName("colorBackgroundLayer")
+ .setCallsite("TaskDisplayArea.onParentChanged")
+ .build();
mAppAnimationLayer = makeChildSurface(null)
.setName("animationLayer")
.setCallsite("TaskDisplayArea.onParentChanged")
@@ -975,6 +999,7 @@
.setName("splitScreenDividerAnchor")
.setCallsite("TaskDisplayArea.onParentChanged")
.build();
+
getSyncTransaction()
.show(mAppAnimationLayer)
.show(mBoostedAppAnimationLayer)
@@ -984,11 +1009,13 @@
} else {
super.onParentChanged(newParent, oldParent);
mWmService.mTransactionFactory.get()
+ .remove(mColorBackgroundLayer)
.remove(mAppAnimationLayer)
.remove(mBoostedAppAnimationLayer)
.remove(mHomeAppAnimationLayer)
.remove(mSplitScreenDividerAnchor)
.apply();
+ mColorBackgroundLayer = null;
mAppAnimationLayer = null;
mBoostedAppAnimationLayer = null;
mHomeAppAnimationLayer = null;
@@ -996,6 +1023,39 @@
}
}
+ void setBackgroundColor(@ColorInt int color) {
+ if (mColorBackgroundLayer == null) {
+ return;
+ }
+
+ float r = ((color >> 16) & 0xff) / 255.0f;
+ float g = ((color >> 8) & 0xff) / 255.0f;
+ float b = ((color >> 0) & 0xff) / 255.0f;
+ float a = ((color >> 24) & 0xff) / 255.0f;
+
+ mColorLayerCounter++;
+
+ getPendingTransaction().setLayer(mColorBackgroundLayer, MIN_VALUE)
+ .setColor(mColorBackgroundLayer, new float[]{r, g, b})
+ .setAlpha(mColorBackgroundLayer, a)
+ .setWindowCrop(mColorBackgroundLayer, getSurfaceWidth(), getSurfaceHeight())
+ .setPosition(mColorBackgroundLayer, 0, 0)
+ .show(mColorBackgroundLayer);
+
+ scheduleAnimation();
+ }
+
+ void clearBackgroundColor() {
+ mColorLayerCounter--;
+
+ // Only clear the color layer if we have received the same amounts of clear as set
+ // requests.
+ if (mColorLayerCounter == 0) {
+ getPendingTransaction().hide(mColorBackgroundLayer);
+ scheduleAnimation();
+ }
+ }
+
@Override
void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
super.migrateToNewSurfaceControl(t);
@@ -1004,6 +1064,7 @@
}
// As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
+ t.reparent(mColorBackgroundLayer, mSurfaceControl);
t.reparent(mAppAnimationLayer, mSurfaceControl);
t.reparent(mBoostedAppAnimationLayer, mSurfaceControl);
t.reparent(mHomeAppAnimationLayer, mSurfaceControl);
@@ -1275,12 +1336,11 @@
for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
final Task launchRootTask = mLaunchRootTasks.get(i).task;
- // Return the focusable root task for improving the UX with staged split screen.
final TaskFragment adjacentTaskFragment = launchRootTask != null
? launchRootTask.getAdjacentTaskFragment() : null;
final Task adjacentRootTask =
adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null;
- if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
+ if (sourceTask != null && sourceTask.getRootTask() == adjacentRootTask) {
return adjacentRootTask;
} else {
return launchRootTask;
@@ -2159,6 +2219,11 @@
}
@Override
+ TaskDisplayArea getTaskDisplayArea() {
+ return this;
+ }
+
+ @Override
boolean isTaskDisplayArea() {
return true;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index a902ca9..fce279d 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -459,6 +459,11 @@
return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment();
}
+ @Nullable
+ Task getRootTask() {
+ return getRootTaskFragment().asTask();
+ }
+
@Override
TaskFragment asTaskFragment() {
return this;
@@ -578,9 +583,6 @@
isPausingDied = true;
}
if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
- if (mLastPausedActivity.isNoHistory()) {
- mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
- }
mLastPausedActivity = null;
}
return isPausingDied;
@@ -2156,6 +2158,13 @@
return mTaskFragmentOrganizer != null;
}
+ boolean isReadyToTransit() {
+ // We don't want to start the transition if the organized TaskFragment is empty, unless
+ // it is requested to be removed.
+ return !isOrganizedTaskFragment() || getTopNonFinishingActivity() != null
+ || mIsRemovalRequested;
+ }
+
/** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
void clearLastPausedActivity() {
forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index f43cd7a..9cc24e2 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -261,7 +261,8 @@
if (launchMode == WINDOWING_MODE_PINNED) {
if (DEBUG) appendLog("picture-in-picture");
} else if (!root.isResizeable()) {
- if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea)) {
+ if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea,
+ options.getLaunchWindowingMode())) {
launchMode = WINDOWING_MODE_FREEFORM;
if (outParams.mBounds.isEmpty()) {
getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
@@ -617,7 +618,11 @@
}
private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
- TaskDisplayArea displayArea) {
+ TaskDisplayArea displayArea, int launchWindowingMode) {
+ if (launchWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // Do not launch the activity in freeform if it explicitly requested fullscreen mode.
+ return false;
+ }
if (!activity.supportsFreeformInDisplayArea(displayArea) || activity.isResizeable()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 28beaf3..ccda126 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -688,6 +688,7 @@
if (state != null) {
state.mOrganizer.onTaskVanished(task);
}
+ mLastSentTaskInfos.remove(task);
break;
case PendingTaskEvent.EVENT_INFO_CHANGED:
dispatchTaskInfoChanged(event.mTask, event.mForce);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1a46d0f..1909875 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -58,6 +58,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
@@ -269,6 +270,18 @@
mChanges.get(wc).mExistenceChanged = true;
}
+ /**
+ * Specifies configuration change explicitly for the window container, so it can be chosen as
+ * transition target. This is usually used with transition mode
+ * {@link android.view.WindowManager#TRANSIT_CHANGE}.
+ */
+ void setKnownConfigChanges(WindowContainer<?> wc, @ActivityInfo.Config int changes) {
+ final ChangeInfo changeInfo = mChanges.get(wc);
+ if (changeInfo != null) {
+ changeInfo.mKnownConfigChanges = changes;
+ }
+ }
+
private void sendRemoteCallback(@Nullable IRemoteCallback callback) {
if (callback == null) return;
mController.mAtm.mH.sendMessage(PooledLambda.obtainMessage(cb -> {
@@ -1218,6 +1231,7 @@
final Rect mAbsoluteBounds = new Rect();
boolean mShowWallpaper;
int mRotation = ROTATION_UNDEFINED;
+ @ActivityInfo.Config int mKnownConfigChanges;
ChangeInfo(@NonNull WindowContainer origState) {
mVisible = origState.isVisibleRequested();
@@ -1240,6 +1254,7 @@
final boolean currVisible = newState.isVisibleRequested();
if (currVisible == mVisible && !mVisible) return false;
return currVisible != mVisible
+ || mKnownConfigChanges != 0
// if mWindowingMode is 0, this container wasn't attached at collect time, so
// assume no change in windowing-mode.
|| (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode)
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index c1d0f80..fc54239 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -71,15 +71,7 @@
final Lock mRunningLock = new Lock();
- private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> {
- // clean-up/finish any playing transitions.
- for (int i = 0; i < mPlayingTransitions.size(); ++i) {
- mPlayingTransitions.get(i).cleanUpOnFailure();
- }
- mPlayingTransitions.clear();
- mTransitionPlayer = null;
- mRunningLock.doNotifyLocked();
- };
+ private final IBinder.DeathRecipient mTransitionPlayerDeath;
/** The transition currently being constructed (collecting participants). */
private Transition mCollectingTransition = null;
@@ -90,6 +82,17 @@
TransitionController(ActivityTaskManagerService atm) {
mAtm = atm;
mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
+ mTransitionPlayerDeath = () -> {
+ synchronized (mAtm.mGlobalLock) {
+ // Clean-up/finish any playing transitions.
+ for (int i = 0; i < mPlayingTransitions.size(); ++i) {
+ mPlayingTransitions.get(i).cleanUpOnFailure();
+ }
+ mPlayingTransitions.clear();
+ mTransitionPlayer = null;
+ mRunningLock.doNotifyLocked();
+ }
+ };
}
/** @see #createTransition(int, int) */
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index ec4eb5d..073a508 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -118,16 +118,30 @@
@Override
public long calculateStatusBarTransitionStartTime() {
TranslateAnimation openTranslateAnimation = findTranslateAnimation(mAnimation);
- if (openTranslateAnimation != null) {
- // Some interpolators are extremely quickly mostly finished, but not completely. For
- // our purposes, we need to find the fraction for which ther interpolator is mostly
- // there, and use that value for the calculation.
- float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
- return SystemClock.uptimeMillis()
- + openTranslateAnimation.getStartOffset()
- + (long)(openTranslateAnimation.getDuration() * t)
- - STATUS_BAR_TRANSITION_DURATION;
+ if (openTranslateAnimation != null) {
+ if (openTranslateAnimation.isXAxisTransition()
+ && openTranslateAnimation.isFullWidthTranslate()) {
+ // On X axis transitions that are fullscreen (heuristic for task like transitions)
+ // we want the status bar to animate right in the middle of the translation when
+ // the windows/tasks have each moved half way across.
+ float t = findMiddleOfTranslationFraction(openTranslateAnimation.getInterpolator());
+
+ return SystemClock.uptimeMillis()
+ + openTranslateAnimation.getStartOffset()
+ + (long) (openTranslateAnimation.getDuration() * t)
+ - (long) (STATUS_BAR_TRANSITION_DURATION * 0.5);
+ } else {
+ // Some interpolators are extremely quickly mostly finished, but not completely. For
+ // our purposes, we need to find the fraction for which their interpolator is mostly
+ // there, and use that value for the calculation.
+ float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
+
+ return SystemClock.uptimeMillis()
+ + openTranslateAnimation.getStartOffset()
+ + (long) (openTranslateAnimation.getDuration() * t)
+ - STATUS_BAR_TRANSITION_DURATION;
+ }
} else {
return SystemClock.uptimeMillis();
}
@@ -176,20 +190,39 @@
}
/**
- * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
- * {@code interpolator(t + eps) > 0.99}.
+ * Finds the fraction of the animation's duration at which the transition is almost done with a
+ * maximal error of 0.01 when it is animated with {@code interpolator}.
*/
private static float findAlmostThereFraction(Interpolator interpolator) {
+ return findInterpolationAdjustedTargetFraction(interpolator, 0.99f, 0.01f);
+ }
+
+ /**
+ * Finds the fraction of the animation's duration at which the transition is spacially half way
+ * done with a maximal error of 0.01 when it is animated with {@code interpolator}.
+ */
+ private float findMiddleOfTranslationFraction(Interpolator interpolator) {
+ return findInterpolationAdjustedTargetFraction(interpolator, 0.5f, 0.01f);
+ }
+
+ /**
+ * Binary searches for a {@code val} such that there exists an {@code -0.01 < epsilon < 0.01}
+ * for which {@code interpolator(val + epsilon) > target}.
+ */
+ private static float findInterpolationAdjustedTargetFraction(
+ Interpolator interpolator, float target, float epsilon) {
float val = 0.5f;
float adj = 0.25f;
- while (adj >= 0.01f) {
- if (interpolator.getInterpolation(val) < 0.99f) {
+
+ while (adj >= epsilon) {
+ if (interpolator.getInterpolation(val) < target) {
val += adj;
} else {
val -= adj;
}
adj /= 2;
}
+
return val;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 26fe2fc..2a8fa10 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -32,6 +32,10 @@
import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
@@ -61,10 +65,13 @@
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
+import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -90,6 +97,7 @@
import android.window.IWindowContainerToken;
import android.window.WindowContainerToken;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
@@ -856,6 +864,12 @@
return parent != null ? parent.getRootDisplayArea() : null;
}
+ @Nullable
+ TaskDisplayArea getTaskDisplayArea() {
+ WindowContainer parent = getParent();
+ return parent != null ? parent.getTaskDisplayArea() : null;
+ }
+
boolean isAttached() {
WindowContainer parent = getParent();
return parent != null && parent.isAttached();
@@ -2320,10 +2334,15 @@
}
protected void setLayer(Transaction t, int layer) {
-
- // Route through surface animator to accommodate that our surface control might be
- // attached to the leash, and leash is attached to parent container.
- mSurfaceAnimator.setLayer(t, layer);
+ if (mSurfaceFreezer.hasLeash()) {
+ // When the freezer has created animation leash parent for the window, set the layer
+ // there instead.
+ mSurfaceFreezer.setLayer(t, layer);
+ } else {
+ // Route through surface animator to accommodate that our surface control might be
+ // attached to the leash, and leash is attached to parent container.
+ mSurfaceAnimator.setLayer(t, layer);
+ }
}
int getLastLayer() {
@@ -2331,10 +2350,15 @@
}
protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
-
- // Route through surface animator to accommodate that our surface control might be
- // attached to the leash, and leash is attached to parent container.
- mSurfaceAnimator.setRelativeLayer(t, relativeTo, layer);
+ if (mSurfaceFreezer.hasLeash()) {
+ // When the freezer has created animation leash parent for the window, set the layer
+ // there instead.
+ mSurfaceFreezer.setRelativeLayer(t, relativeTo, layer);
+ } else {
+ // Route through surface animator to accommodate that our surface control might be
+ // attached to the leash, and leash is attached to parent container.
+ mSurfaceAnimator.setRelativeLayer(t, relativeTo, layer);
+ }
}
protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
@@ -2551,10 +2575,13 @@
* some point but the meaning is too weird to work for all containers.
* @param type The type of animation defined as {@link AnimationType}.
* @param animationFinishedCallback The callback being triggered when the animation finishes.
+ * @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
+ * cancel call to the underlying AnimationAdapter.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
- @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback,
+ @Nullable Runnable animationCancelledCallback) {
if (DEBUG_ANIM) {
Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
}
@@ -2562,7 +2589,14 @@
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
- mSurfaceFreezer);
+ animationCancelledCallback, mSurfaceFreezer);
+ }
+
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
+ @AnimationType int type,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ startAnimation(t, anim, hidden, type, animationFinishedCallback,
+ null /* adapterAnimationCancelledCallback */);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -2589,11 +2623,20 @@
* 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}.
*
* This shouldn't be called on other {@link WindowContainer} unless there is a valid use case.
+ *
+ * @param startBounds The original bounds (on screen) of the surface we are snapshotting.
+ * @param parentBounds The parent bounds (on screen) to calculate the animation surface
+ * position.
*/
- void initializeChangeTransition(Rect startBounds) {
+ void initializeChangeTransition(Rect startBounds, Rect parentBounds) {
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
mDisplayContent.mChangingContainers.add(this);
- mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
+ mTmpPoint.set(startBounds.left - parentBounds.left, startBounds.top - parentBounds.top);
+ mSurfaceFreezer.freeze(getSyncTransaction(), startBounds, mTmpPoint);
+ }
+
+ void initializeChangeTransition(Rect startBounds) {
+ initializeChangeTransition(startBounds, getParent().getBounds());
}
ArraySet<WindowContainer> getAnimationSources() {
@@ -2796,8 +2839,26 @@
if (sources != null) {
mSurfaceAnimationSources.addAll(sources);
}
+
+ TaskDisplayArea taskDisplayArea = getTaskDisplayArea();
+ boolean isSettingBackgroundColor = taskDisplayArea != null
+ && isTransitionWithBackgroundColor(transit);
+
+ if (isSettingBackgroundColor) {
+ Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+ @ColorInt int backgroundColor = uiContext.getColor(R.color.overview_background);
+
+ taskDisplayArea.setBackgroundColor(backgroundColor);
+ }
+
+ final Runnable cleanUpCallback = isSettingBackgroundColor
+ ? taskDisplayArea::clearBackgroundColor : () -> {};
+
startAnimation(getPendingTransaction(), adapter, !isVisible(),
- ANIMATION_TYPE_APP_TRANSITION);
+ ANIMATION_TYPE_APP_TRANSITION,
+ (type, anim) -> cleanUpCallback.run(),
+ cleanUpCallback);
+
if (adapter.getShowWallpaper()) {
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
@@ -2808,6 +2869,13 @@
}
}
+ private boolean isTransitionWithBackgroundColor(@TransitionOldType int transit) {
+ return transit == TRANSIT_OLD_TASK_OPEN
+ || transit == TRANSIT_OLD_TASK_CLOSE
+ || transit == TRANSIT_OLD_TASK_TO_FRONT
+ || transit == TRANSIT_OLD_TASK_TO_BACK;
+ }
+
final SurfaceAnimationRunner getSurfaceAnimationRunner() {
return mWmService.mSurfaceAnimationRunner;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 834b6e6..6931381 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -122,7 +122,8 @@
* A Map which manages the relationship between
* {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment}
*/
- private final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
+ @VisibleForTesting
+ final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
WindowOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
@@ -317,7 +318,7 @@
* @param caller Info about the calling process.
*/
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
- @Nullable Transition transition, @Nullable CallerInfo caller) {
+ @Nullable Transition transition, @NonNull CallerInfo caller) {
int effects = 0;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
@@ -326,7 +327,7 @@
if (transition != null) {
// First check if we have a display rotation transition and if so, update it.
final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition);
- if (dc != null && transition.mChanges.get(dc).mRotation != dc.getRotation()) {
+ if (dc != null && transition.mChanges.get(dc).hasChanged(dc)) {
// Go through all tasks and collect them before the rotation
// TODO(shell-transitions): move collect() to onConfigurationChange once
// wallpaper handling is synchronized.
@@ -539,7 +540,7 @@
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
- @Nullable CallerInfo caller, @Nullable IBinder errorCallbackToken,
+ @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
@Nullable ITaskFragmentOrganizer organizer) {
final int type = hop.getType();
switch (type) {
@@ -627,11 +628,28 @@
final int taskId = launchOpts.getInt(
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- final SafeActivityOptions safeOptions = caller != null
- ? SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid)
- : SafeActivityOptions.fromBundle(launchOpts);
- mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid,
- taskId, safeOptions);
+ final SafeActivityOptions safeOptions =
+ SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
+ final Integer[] starterResult = { null };
+ // startActivityFromRecents should not be called in lock.
+ mService.mH.post(() -> {
+ try {
+ starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents(
+ caller.mPid, caller.mUid, taskId, safeOptions);
+ } catch (Throwable t) {
+ starterResult[0] = ActivityManager.START_CANCELED;
+ Slog.w(TAG, t);
+ }
+ synchronized (mGlobalLock) {
+ mGlobalLock.notifyAll();
+ }
+ });
+ while (starterResult[0] == null) {
+ try {
+ mGlobalLock.wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
break;
case HIERARCHY_OP_TYPE_PENDING_INTENT:
String resolvedType = hop.getActivityIntent() != null
@@ -672,7 +690,7 @@
throw new IllegalArgumentException(
"Can only delete organized TaskFragment, but not Task.");
}
- deleteTaskFragment(taskFragment, errorCallbackToken);
+ effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
break;
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
fragmentToken = hop.getContainer();
@@ -704,6 +722,7 @@
break;
}
activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
@@ -716,6 +735,7 @@
break;
}
reparentTaskFragment(oldParent, newParent, errorCallbackToken);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
fragmentToken = hop.getContainer();
@@ -731,6 +751,7 @@
break;
}
tf1.setAdjacentTaskFragment(tf2);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
final Bundle bundle = hop.getLaunchOptions();
final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
@@ -1175,7 +1196,7 @@
}
}
- void deleteTaskFragment(@NonNull TaskFragment taskFragment,
+ private int deleteTaskFragment(@NonNull TaskFragment taskFragment,
@Nullable IBinder errorCallbackToken) {
final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
if (index < 0) {
@@ -1184,10 +1205,11 @@
+ "taskFragment");
sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
errorCallbackToken, exception);
- return;
+ return 0;
}
mLaunchTaskFragments.removeAt(index);
taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
+ return TRANSACT_EFFECTS_LIFECYCLE;
}
@Nullable
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 6eb2e8a..719d52c 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -264,7 +264,7 @@
}
onConfigurationChanged(atm.getGlobalConfiguration());
- mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mName);
+ mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName);
}
public void setPid(int pid) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2ccbf40..c3d51d5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -26,7 +26,6 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.graphics.GraphicsProtos.dumpPointProto;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
-import static android.hardware.input.InputManager.BLOCK_UNTRUSTED_TOUCHES;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -196,7 +195,6 @@
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyCache;
-import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Matrix;
@@ -1172,9 +1170,6 @@
}
int getTouchOcclusionMode() {
- if (!CompatChanges.isChangeEnabled(BLOCK_UNTRUSTED_TOUCHES, mOwnerUid)) {
- return TouchOcclusionMode.ALLOW;
- }
if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) {
return TouchOcclusionMode.USE_OPACITY;
}
@@ -2582,8 +2577,7 @@
}
}
final boolean isAnimating = mAnimatingExit
- || isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES)
- && (mActivityRecord == null || !mActivityRecord.isWaitingForTransitionStart());
+ || isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES);
final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
&& mActivityRecord.isLastWindow(this);
// We delay the removal of a window if it has a showing surface that can be used to run
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6b4b0c9..c283ef0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8414,17 +8414,19 @@
mDeviceAdminServiceController.startServiceForOwner(
admin.getPackageName(), userId, "set-device-owner");
- Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
+ Slogf.i(LOG_TAG, "Device owner set: %s on user %d", admin.flattenToShortString(),
+ userId);
if (mInjector.userManagerIsHeadlessSystemUserMode()) {
int currentForegroundUser = getCurrentForegroundUserId();
- Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin
- + " as profile owner on user " + currentForegroundUser);
+ Slogf.i(LOG_TAG, "setDeviceOwner(): setting %s as profile owner on user %d",
+ admin.flattenToShortString(), currentForegroundUser);
// Sets profile owner on current foreground user since
// the human user will complete the DO setup workflow from there.
- manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
+ mInjector.binderWithCleanCallingIdentity(() -> manageUserUnchecked(
+ /* deviceOwner= */ admin, /* profileOwner= */ admin,
/* managedUser= */ currentForegroundUser, /* adminExtras= */ null,
- /* showDisclaimer= */ false);
+ /* showDisclaimer= */ false));
}
return true;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index fd364ae7..51fa851 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -37,6 +37,8 @@
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Bundle;
import android.os.RecoverySystem;
@@ -83,6 +85,8 @@
private static final String CALLING_PACKAGE1 = "com.package.name1";
private static final String CALLING_PACKAGE2 = "com.package.name2";
private static final String CALLING_PACKAGE3 = "com.package.name3";
+ private static final String PERSISTENT_PACKAGE = "com.persistent.package";
+ private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package";
private static final String NAMESPACE1 = "namespace1";
private static final String NAMESPACE2 = "namespace2";
private static final String NAMESPACE3 = "namespace3";
@@ -103,6 +107,8 @@
private PackageWatchdog mMockPackageWatchdog;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ContentResolver mMockContentResolver;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PackageManager mPackageManager;
@Captor
private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor;
@@ -129,6 +135,17 @@
mNamespacesWiped = new HashSet<>();
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+ when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+ ApplicationInfo persistentApplicationInfo = new ApplicationInfo();
+ persistentApplicationInfo.flags |=
+ ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT;
+
+ // If the package name is PERSISTENT_PACKAGE, then set the flags to be persistent and
+ // system. Don't set any flags otherwise.
+ when(mPackageManager.getApplicationInfo(eq(PERSISTENT_PACKAGE),
+ anyInt())).thenReturn(persistentApplicationInfo);
+ when(mPackageManager.getApplicationInfo(eq(NON_PERSISTENT_PACKAGE),
+ anyInt())).thenReturn(new ApplicationInfo());
// Reset observer instance to get new mock context on every run
RescuePartyObserver.reset();
@@ -241,29 +258,53 @@
@Test
public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
- notePersistentAppCrash(1);
+ noteAppCrash(1, true);
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
/*configResetVerifiedTimesMap=*/ null);
- notePersistentAppCrash(2);
+ noteAppCrash(2, true);
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
/*configResetVerifiedTimesMap=*/ null);
- notePersistentAppCrash(3);
+ noteAppCrash(3, true);
verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
/*configResetVerifiedTimesMap=*/ null);
- notePersistentAppCrash(4);
+ noteAppCrash(4, true);
assertTrue(RescueParty.isRebootPropertySet());
- notePersistentAppCrash(5);
+ noteAppCrash(5, true);
assertTrue(RescueParty.isFactoryResetPropertySet());
}
@Test
+ public void testNonPersistentAppOnlyPerformsFlagResets() {
+ noteAppCrash(1, false);
+
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
+ /*configResetVerifiedTimesMap=*/ null);
+
+ noteAppCrash(2, false);
+
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
+ /*configResetVerifiedTimesMap=*/ null);
+
+ noteAppCrash(3, false);
+
+ verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
+ /*configResetVerifiedTimesMap=*/ null);
+
+ noteAppCrash(4, false);
+ assertFalse(RescueParty.isRebootPropertySet());
+
+ noteAppCrash(5, false);
+ assertFalse(RescueParty.isFactoryResetPropertySet());
+ }
+
+ @Test
public void testNonPersistentAppCrashDetectionWithScopedResets() {
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
@@ -311,11 +352,11 @@
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
- assertTrue(RescueParty.isRebootPropertySet());
+ assertFalse(RescueParty.isRebootPropertySet());
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
- assertTrue(RescueParty.isFactoryResetPropertySet());
+ assertFalse(RescueParty.isFactoryResetPropertySet());
}
@Test
@@ -376,11 +417,11 @@
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
- assertTrue(RescueParty.isRebootPropertySet());
+ assertFalse(RescueParty.isRebootPropertySet());
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
- assertTrue(RescueParty.isFactoryResetPropertySet());
+ assertFalse(RescueParty.isFactoryResetPropertySet());
}
@Test
@@ -627,9 +668,10 @@
RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount);
}
- private void notePersistentAppCrash(int mitigationCount) {
+ private void noteAppCrash(int mitigationCount, boolean isPersistent) {
+ String packageName = isPersistent ? PERSISTENT_PACKAGE : NON_PERSISTENT_PACKAGE;
RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
- "com.package.name", 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
+ packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
}
private Bundle getConfigAccessBundle(String callingPackage, String namespace) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 022fadc..609768c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -21,12 +21,16 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import android.app.ActivityManager;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -48,8 +52,14 @@
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
-import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -60,8 +70,11 @@
* Build/Install/Run:
* atest FrameworksMockingServicesTests:CacheOomRankerTest
*/
+@SuppressWarnings("GuardedBy") // No tests are concurrent, so no need to test locking.
@RunWith(MockitoJUnitRunner.class)
public class CacheOomRankerTest {
+ private static final Instant NOW = LocalDate.of(2021, 1, 1).atStartOfDay(
+ ZoneOffset.UTC).toInstant();
@Mock
private AppOpsService mAppOpsService;
@@ -82,6 +95,7 @@
private int mNextUid = 30000;
private int mNextPackageUid = 40000;
private int mNextPackageName = 1;
+ private Map<Integer, Long> mPidToRss;
private TestExecutor mExecutor = new TestExecutor();
private CacheOomRanker mCacheOomRanker;
@@ -107,7 +121,15 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
- mCacheOomRanker = new CacheOomRanker(mAms);
+ mPidToRss = new HashMap<>();
+ mCacheOomRanker = new CacheOomRanker(
+ mAms,
+ pid -> {
+ Long rss = mPidToRss.get(pid);
+ assertThat(rss).isNotNull();
+ return new long[]{rss};
+ }
+ );
mCacheOomRanker.init(mExecutor);
}
@@ -136,6 +158,15 @@
mExecutor.init();
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+ Integer.toString(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1),
+ false);
+ mExecutor.waitForLatch();
+ assertThat(mCacheOomRanker.mPreserveTopNApps)
+ .isEqualTo(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1);
+
+ mExecutor.init();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f),
false);
@@ -165,6 +196,9 @@
@Test
public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 5,
+ /* preserveTopNApps= */ 0,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
/* usesWeight= */ 0.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */1.0f);
@@ -172,36 +206,40 @@
ProcessList list = new ProcessList();
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(lastUsed40MinutesAgo);
ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(lastUsed42MinutesAgo);
ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+ NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
processList.add(lastUsed60MinutesAgo);
ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(lastUsed15MinutesAgo);
ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
processList.add(lastUsed17MinutesAgo);
// Only re-ranking 5 entries so this should stay in most recent position.
ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
processList.add(lastUsed30MinutesAgo);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 5 ordered by least recently used first, then last processes position unchanged.
assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo,
- lastUsed30MinutesAgo);
+ lastUsed30MinutesAgo).inOrder();
}
@Test
public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 0,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
/* usesWeight= */ 0.0f,
/* pssWeight= */ 1.0f,
/* lruWeight= */ 0.0f);
@@ -209,145 +247,459 @@
ProcessList list = new ProcessList();
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(rss10k);
ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(rss20k);
ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+ NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
processList.add(rss1k);
ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(rss100k);
ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(rss2k);
ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
processList.add(rss15k);
// Only re-ranking 6 entries so this should stay in most recent position.
ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
processList.add(rss16k);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 6 ordered by largest pss, then last processes position unchanged.
assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
- rss16k);
+ rss16k).inOrder();
}
@Test
+ public void reRankLruCachedApps_rssImpactsOrdering_cachedRssValues()
+ throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 0,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 10000000,
+ /* usesWeight= */ 0.0f,
+ /* pssWeight= */ 1.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(rss10k);
+ ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(rss20k);
+ ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
+ processList.add(rss1k);
+ ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(rss100k);
+ ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(rss2k);
+ ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
+ processList.add(rss15k);
+ // Only re-ranking 6 entries so this should stay in most recent position.
+ ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
+ processList.add(rss16k);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+ // First 6 ordered by largest pss, then last processes position unchanged.
+ assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+ rss16k).inOrder();
+
+ // Clear mPidToRss so that Process.getRss calls fail.
+ mPidToRss.clear();
+ // Mix up the process list to ensure that CacheOomRanker actually re-ranks.
+ Collections.swap(processList, 0, 1);
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+ // Re ranking is the same.
+ assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+ rss16k).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_rssImpactsOrdering_profileRss()
+ throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 0,
+ /* useFrequentRss= */ false,
+ /* rssUpdateRateMs= */ 10000000,
+ /* usesWeight= */ 0.0f,
+ /* pssWeight= */ 1.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 0L, 1000);
+ rss10k.mProfile.setLastRss(10 * 1024L);
+ processList.add(rss10k);
+ ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 0L, 2000);
+ rss20k.mProfile.setLastRss(20 * 1024L);
+ processList.add(rss20k);
+ ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 0L, 10000);
+ rss1k.mProfile.setLastRss(1024L);
+ processList.add(rss1k);
+ ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 0L, 10);
+ rss100k.mProfile.setLastRss(100 * 1024L);
+ processList.add(rss100k);
+ ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 0L, 20);
+ rss2k.mProfile.setLastRss(2 * 1024L);
+ processList.add(rss2k);
+ ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
+ rss15k.mProfile.setLastRss(15 * 1024L);
+ processList.add(rss15k);
+ // Only re-ranking 6 entries so this should stay in most recent position.
+ ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
+ rss16k.mProfile.setLastRss(16 * 1024L);
+ processList.add(rss16k);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ // This should not be used, as RSS values are taken from mProfile.
+ mPidToRss.clear();
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+ // First 6 ordered by largest pss, then last processes position unchanged.
+ assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+ rss16k).inOrder();
+
+ // Clear mPidToRss so that Process.getRss calls fail.
+ mPidToRss.clear();
+ // Mix up the process list to ensure that CacheOomRanker actually re-ranks.
+ Collections.swap(processList, 0, 1);
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+ // Re ranking is the same.
+ assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+ rss16k).inOrder();
+ }
+
+
+ @Test
public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 4,
+ /* preserveTopNApps= */ 0,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
/* usesWeight= */ 1.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- list.setLruProcessServiceStartLSP(1);
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(used1000);
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(used2000);
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(used10);
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(used20);
// Only re-ranking 6 entries so last two should stay in most recent position.
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
processList.add(used500);
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 4 ordered by uses, then last processes position unchanged.
assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
- used200);
+ used200).inOrder();
}
@Test
- public void reRankLruCachedApps_notEnoughProcesses() throws InterruptedException {
+ public void reRankLruCachedApps_fewProcesses() throws InterruptedException {
setConfig(/* numberToReRank= */ 4,
- /* usesWeight= */ 0.5f,
- /* pssWeight= */ 0.2f,
- /* lruWeight= */ 0.3f);
-
- ProcessList list = new ProcessList();
- ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
- ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
- processList.add(unknownAdj1);
- ProcessRecord unknownAdj2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
- processList.add(unknownAdj2);
- ProcessRecord unknownAdj3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
- processList.add(unknownAdj3);
- ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
- processList.add(foregroundAdj);
- ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
- processList.add(serviceAdj);
- ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
- processList.add(systemAdj);
-
- // 6 Processes but only 3 in eligible for cache so no re-ranking.
- mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
-
- // All positions unchanged.
- assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3,
- foregroundAdj, serviceAdj, systemAdj);
- }
-
- @Test
- public void reRankLruCachedApps_notEnoughNonServiceProcesses() throws InterruptedException {
- setConfig(/* numberToReRank= */ 4,
+ /* preserveTopNApps= */ 0,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
/* usesWeight= */ 1.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- list.setLruProcessServiceStartLSP(4);
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(used1000);
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(used2000);
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(used10);
- ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
- processList.add(used20);
- ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
- processList.add(used500);
- ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
- processList.add(used200);
+ ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(foregroundAdj);
+ ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(serviceAdj);
+ ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(systemAdj);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
- // All positions unchanged.
- assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
- used200);
+ // 6 processes, only 3 in eligible for cache, so only those are re-ranked.
+ assertThat(processList).containsExactly(used10, used1000, used2000,
+ foregroundAdj, serviceAdj, systemAdj).inOrder();
}
- private void setConfig(int numberToReRank, float useWeight, float pssWeight, float lruWeight)
+ @Test
+ public void reRankLruCachedApps_fewNonServiceProcesses() throws InterruptedException {
+ setConfig(/* numberToReRank= */ 4,
+ /* preserveTopNApps= */ 0,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ ProcessRecord service1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(service1);
+ ProcessRecord service2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(service2);
+ ProcessRecord service3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(service3);
+ list.setLruProcessServiceStartLSP(3);
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // Services unchanged, rest re-ranked.
+ assertThat(processList).containsExactly(used10, used1000, used2000, service1, service2,
+ service3).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_manyProcessesThenFew() throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 0,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList set1List = new ProcessList();
+ ArrayList<ProcessRecord> set1ProcessList = set1List.getLruProcessesLSP();
+ ProcessRecord set1Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ set1ProcessList.add(set1Used1000);
+ ProcessRecord set1Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ set1ProcessList.add(set1Used2000);
+ ProcessRecord set1Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ set1ProcessList.add(set1Used10);
+ ProcessRecord set1Uses20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ set1ProcessList.add(set1Uses20);
+ ProcessRecord set1Uses500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ set1ProcessList.add(set1Uses500);
+ ProcessRecord set1Uses200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ set1ProcessList.add(set1Uses200);
+ set1List.setLruProcessServiceStartLSP(set1ProcessList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(set1ProcessList,
+ set1List.getLruProcessServiceStartLOSP());
+ assertThat(set1ProcessList).containsExactly(set1Used10, set1Uses20, set1Uses200,
+ set1Uses500, set1Used1000, set1Used2000).inOrder();
+
+ ProcessList set2List = new ProcessList();
+ ArrayList<ProcessRecord> set2ProcessList = set2List.getLruProcessesLSP();
+ ProcessRecord set2Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ set2ProcessList.add(set2Used1000);
+ ProcessRecord set2Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ set2ProcessList.add(set2Used2000);
+ ProcessRecord set2Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ set2ProcessList.add(set2Used10);
+ ProcessRecord set2ForegroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ set2ProcessList.add(set2ForegroundAdj);
+ ProcessRecord set2ServiceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ set2ProcessList.add(set2ServiceAdj);
+ ProcessRecord set2SystemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ set2ProcessList.add(set2SystemAdj);
+ set2List.setLruProcessServiceStartLSP(set2ProcessList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(set2ProcessList,
+ set2List.getLruProcessServiceStartLOSP());
+ assertThat(set2ProcessList).containsExactly(set2Used10, set2Used1000, set2Used2000,
+ set2ForegroundAdj, set2ServiceAdj, set2SystemAdj).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps() throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 3,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ // Preserving the top 3 processes, so these should not be re-ranked.
+ ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(used20);
+ ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(used500);
+ ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // First 3 ordered by uses, then last processes position unchanged.
+ assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+ used200).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps_allAppsUnchanged()
+ throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 100,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(used20);
+ ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(used500);
+ ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // Nothing reordered, as we preserve the top 100 apps.
+ assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
+ used200).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps_negativeReplacedWithDefault()
+ throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ -100,
+ /* useFrequentRss= */ true,
+ /* rssUpdateRateMs= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ // Negative preserveTopNApps interpreted as the default (3), so the last three are unranked.
+ ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(used20);
+ ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(used500);
+ ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // First 3 apps re-ranked, as preserveTopNApps is interpreted as 3.
+ assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+ used200).inOrder();
+ }
+
+ private void setConfig(int numberToReRank, int preserveTopNApps, boolean useFrequentRss,
+ long rssUpdateRateMs, float usesWeight, float pssWeight, float lruWeight)
throws InterruptedException {
mExecutor.init(4);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -355,6 +707,18 @@
Integer.toString(numberToReRank),
false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+ Integer.toString(preserveTopNApps),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CacheOomRanker.KEY_OOM_RE_RANKING_USE_FREQUENT_RSS,
+ Boolean.toString(useFrequentRss),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CacheOomRanker.KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS,
+ Long.toString(rssUpdateRateMs),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
Float.toString(lruWeight),
false);
@@ -364,17 +728,19 @@
false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT,
- Float.toString(useWeight),
+ Float.toString(usesWeight),
false);
mExecutor.waitForLatch();
assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank);
+ assertThat(mCacheOomRanker.mUseFrequentRss).isEqualTo(useFrequentRss);
+ assertThat(mCacheOomRanker.mRssUpdateRateMs).isEqualTo(rssUpdateRateMs);
assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight);
- assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(useWeight);
+ assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(usesWeight);
assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight);
}
private ProcessRecord nextProcessRecord(int setAdj, long lastActivityTime, long lastRss,
- int returnedToCacheCount) {
+ int wentToForegroundCount) {
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = "a.package.name" + mNextPackageName++;
ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++);
@@ -382,14 +748,20 @@
app.info.uid = mNextPackageUid++;
// Exact value does not mater, it can be any state for which compaction is allowed.
app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- app.mState.setSetAdj(setAdj);
+ app.mState.setCurAdj(setAdj);
app.setLastActivityTime(lastActivityTime);
- app.mProfile.setLastRss(lastRss);
+ mPidToRss.put(app.getPid(), lastRss);
app.mState.setCached(false);
- for (int i = 0; i < returnedToCacheCount; ++i) {
- app.mState.setCached(false);
- app.mState.setCached(true);
+ for (int i = 0; i < wentToForegroundCount; ++i) {
+ app.mState.setSetProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ app.mState.setSetProcState(ActivityManager.PROCESS_STATE_CACHED_RECENT);
}
+ // Sets the thread returned by ProcessRecord#getThread, which we use to check whether the
+ // app is currently launching.
+ ProcessStatsService processStatsService = new ProcessStatsService(
+ mock(ActivityManagerService.class), new File(Environment.getDataSystemCeDirectory(),
+ "procstats"));
+ app.makeActive(mock(IApplicationThread.class), processStatsService);
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
index bf97042..df19be4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
@@ -31,8 +31,6 @@
import androidx.test.filters.SmallTest;
-import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -183,115 +181,84 @@
}
@Test
- public void parsePackageOverrides_emptyConfig_returnsEmpty() {
- PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
- "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+ public void parsePackageOverrides_emptyConfigNoOwnedChangeIds_returnsEmpty() {
+ Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
+ /* configStr= */ "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
- assertThat(result.overridesToAdd).isEmpty();
- assertThat(result.overridesToRemove).isEmpty();
+ assertThat(result).isEmpty();
}
@Test
public void parsePackageOverrides_configWithSingleOverride_returnsOverride() {
- PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
- "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
-
- assertThat(result.overridesToAdd).hasSize(1);
- assertThat(result.overridesToAdd.get(123L)).isEqualTo(
- new PackageOverride.Builder().setEnabled(true).build());
- }
-
- @Test
- public void parsePackageOverrides_configWithMultipleOverridesToAdd_returnsOverrides() {
- PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
- "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true,56::2:true,"
- + "56:3:4:false,34:4:8:true,78:6:7:true,910:5::true,1112::5:true,"
- + "56:6::true,1112:6:7:false", /* versionCode= */
- 5, /* changeIdsToSkip= */ emptySet());
-
- assertThat(result.overridesToAdd).hasSize(6);
- assertThat(result.overridesToAdd.get(12L)).isEqualTo(
- new PackageOverride.Builder().setEnabled(false).build());
- assertThat(result.overridesToAdd.get(34L)).isEqualTo(
- new PackageOverride.Builder().setMinVersionCode(4).setMaxVersionCode(8).setEnabled(
- true).build());
- assertThat(result.overridesToAdd.get(56L)).isEqualTo(
- new PackageOverride.Builder().setMinVersionCode(3).setMaxVersionCode(4).setEnabled(
- false).build());
- assertThat(result.overridesToAdd.get(78L)).isEqualTo(
- new PackageOverride.Builder().setMinVersionCode(6).setMaxVersionCode(7).setEnabled(
- true).build());
- assertThat(result.overridesToAdd.get(910L)).isEqualTo(
- new PackageOverride.Builder().setMinVersionCode(5).setEnabled(true).build());
- assertThat(result.overridesToAdd.get(1112L)).isEqualTo(
- new PackageOverride.Builder().setMaxVersionCode(5).setEnabled(true).build());
- assertThat(result.overridesToRemove).isEmpty();
- }
-
- @Test
- public void parsePackageOverrides_configWithMultipleOverridesToRemove_returnsOverrides() {
- PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
- "12:::,34:1:2:", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
-
- assertThat(result.overridesToRemove).containsExactly(12L, 34L);
- assertThat(result.overridesToAdd).isEmpty();
- }
-
- @Test
- public void parsePackageOverrides_configWithBothOverridesToAddAndRemove_returnsOverrides() {
- // Note that change 56 is both added and removed, therefore it will only be removed.
- PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
- "56:::,12:::true,34:::,56:3:7:true", /* versionCode= */ 5, /* changeIdsToSkip= */
+ Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
+ /* configStr= */ "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */
emptySet());
- assertThat(result.overridesToAdd).hasSize(1);
- assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ assertThat(result).hasSize(1);
+ assertThat(result.get(123L)).isEqualTo(
new PackageOverride.Builder().setEnabled(true).build());
- assertThat(result.overridesToRemove).containsExactly(34L, 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithMultipleOverrides_returnsOverrides() {
+ Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
+ /* configStr= */ "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true,"
+ + "56::2:true,56:3:4:false,34:4:8:true,78:6:7:true,910:5::true,"
+ + "1112::5:true,56:6::true,1112:6:7:false", /* versionCode= */
+ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result).hasSize(6);
+ assertThat(result.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(false).build());
+ assertThat(result.get(34L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(4).setMaxVersionCode(8).setEnabled(
+ true).build());
+ assertThat(result.get(56L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(3).setMaxVersionCode(4).setEnabled(
+ false).build());
+ assertThat(result.get(78L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(6).setMaxVersionCode(7).setEnabled(
+ true).build());
+ assertThat(result.get(910L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(5).setEnabled(true).build());
+ assertThat(result.get(1112L)).isEqualTo(
+ new PackageOverride.Builder().setMaxVersionCode(5).setEnabled(true).build());
}
@Test
public void parsePackageOverrides_changeIdsToSkipSpecified_returnsWithoutChangeIdsToSkip() {
- ArraySet<Long> changeIdsToSkip = new ArraySet<>();
- changeIdsToSkip.add(34L);
- changeIdsToSkip.add(56L);
- changeIdsToSkip.add(910L);
- PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
- "12:::true,34:::,56:3:7:true,78:::", /* versionCode= */ 5, changeIdsToSkip);
+ ArraySet<Long> changeIdsToSkip = new ArraySet<>(Arrays.asList(34L, 56L));
+ Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
+ /* configStr= */ "12:::true,56:3:7:true", /* versionCode= */ 5, changeIdsToSkip);
- assertThat(result.overridesToAdd).hasSize(1);
- assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ assertThat(result).hasSize(1);
+ assertThat(result.get(12L)).isEqualTo(
new PackageOverride.Builder().setEnabled(true).build());
- assertThat(result.overridesToRemove).containsExactly(78L);
}
@Test
public void parsePackageOverrides_changeIdsToSkipContainsAllIds_returnsEmpty() {
- ArraySet<Long> changeIdsToSkip = new ArraySet<>();
- changeIdsToSkip.add(12L);
- changeIdsToSkip.add(34L);
- PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
- "12:::true,34:::", /* versionCode= */ 5, changeIdsToSkip);
+ ArraySet<Long> changeIdsToSkip = new ArraySet<>(Arrays.asList(12L, 34L));
+ Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
+ /* configStr= */ "12:::true", /* versionCode= */ 5, changeIdsToSkip);
- assertThat(result.overridesToAdd).isEmpty();
- assertThat(result.overridesToRemove).isEmpty();
+ assertThat(result).isEmpty();
}
@Test
public void parsePackageOverrides_someOverridesAreInvalid_returnsWithoutInvalidOverrides() {
// We add a valid entry before and after the invalid ones to make sure they are applied.
- PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
- "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no,"
- + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:one:ten:",
+ Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
+ /* configStr= */ "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no,"
+ + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:::",
/* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
- assertThat(result.overridesToAdd).hasSize(2);
- assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ assertThat(result).hasSize(2);
+ assertThat(result.get(12L)).isEqualTo(
new PackageOverride.Builder().setEnabled(true).build());
- assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+ assertThat(result.get(56L)).isEqualTo(
new PackageOverride.Builder().setMinVersionCode(1).setMaxVersionCode(2).setEnabled(
false).build());
- assertThat(result.overridesToRemove).containsExactly(34L);
}
private static ApplicationInfo createAppInfo(String packageName) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
index 3129272..007191f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -95,7 +95,6 @@
private static final String PACKAGE_2 = "com.android.test2";
private static final String PACKAGE_3 = "com.android.test3";
private static final String PACKAGE_4 = "com.android.test4";
- private static final String PACKAGE_5 = "com.android.test5";
private MockContext mMockContext;
private BroadcastReceiver mPackageReceiver;
@@ -157,16 +156,14 @@
mockGetApplicationInfoNotInstalled(PACKAGE_2);
mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 10);
mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 1);
- mockGetApplicationInfo(PACKAGE_5, /* versionCode= */ 1);
mService.registerDeviceConfigListeners();
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
- .setString(PACKAGE_1, "123:::true,456::1:false,456:2::true")
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789")
+ .setString(PACKAGE_1, "123:::true,456::1:false,456:2::true,789:::false")
.setString(PACKAGE_2, "123:::true")
- .setString(PACKAGE_3, "123:1:9:true,123:10:11:false,123:11::true,456:::")
- .setString(PACKAGE_4, "")
- .setString(PACKAGE_5, "123:::,789:::")
- .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789").build());
+ .setString(PACKAGE_3, "123:1:9:true,123:10:11:false,123:11::true")
+ .setString(PACKAGE_4, "").build());
Map<Long, PackageOverride> addedOverrides;
// Package 1
@@ -175,11 +172,13 @@
verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
- assertThat(addedOverrides).hasSize(2);
+ assertThat(addedOverrides).hasSize(3);
assertThat(addedOverrides.get(123L)).isEqualTo(
new PackageOverride.Builder().setEnabled(true).build());
assertThat(addedOverrides.get(456L)).isEqualTo(
new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build());
+ assertThat(addedOverrides.get(789L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(false).build());
// Package 2
verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
@@ -195,24 +194,37 @@
assertThat(addedOverrides.get(123L)).isEqualTo(
new PackageOverride.Builder().setMinVersionCode(10).setMaxVersionCode(
11).setEnabled(false).build());
- assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L, 789L);
// Package 4
verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
- verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
- any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
- // Package 5
- verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
- any(CompatibilityOverrideConfig.class), eq(PACKAGE_5));
verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
- mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_5));
- assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 789L);
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_4));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L,
+ 789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_ownedChangeIdsFlagNotSet_onlyAddsOverrides()
+ throws Exception {
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true").build());
+
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L);
}
@Test
public void onPropertiesChanged_removeOverridesFlagSetBefore_skipsOverridesToRemove()
throws Exception {
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789")
.setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456," + PACKAGE_2 + "=123")
.setString(PACKAGE_1, "123:::true")
.setString(PACKAGE_4, "123:::true").build());
@@ -222,7 +234,7 @@
mService.registerDeviceConfigListeners();
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
- .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+ .setString(PACKAGE_1, "123:::true,789:::false")
.setString(PACKAGE_2, "123:::true")
.setString(PACKAGE_3, "456:::true").build());
@@ -235,14 +247,16 @@
// Package 2
verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
- verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
- any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L, 789L);
// Package 3
verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
eq(PACKAGE_3));
- verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
- any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 789L);
// Package 4 (not applied because it hasn't changed after the listener was added)
verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
@@ -253,27 +267,44 @@
@Test
public void onPropertiesChanged_removeOverridesFlagChangedNoPackageOverridesFlags_removesOnly()
throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789")
+ .setString(PACKAGE_1, "")
+ .setString(PACKAGE_2, "").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+
mService.registerDeviceConfigListeners();
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
.setString(FLAG_REMOVE_OVERRIDES,
- PACKAGE_1 + "=123:456," + PACKAGE_2 + "=789").build());
+ PACKAGE_1 + "=123:456," + PACKAGE_2 + "=*").build());
// Package 1
- verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
- assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L);
+ List<CompatibilityOverridesToRemoveConfig> configs =
+ mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.size()).isAtLeast(2);
+ assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(123L, 456L);
+ assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(789L);
// Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
- assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L,
+ 789L);
}
@Test
public void onPropertiesChanged_removeOverridesFlagAndSomePackageOverrideFlagsChanged_ok()
throws Exception {
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789")
.setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456")
- .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+ .setString(PACKAGE_1, "123:::true,789:::false")
.setString(PACKAGE_3, "456:::false,789:::true").build());
mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
@@ -282,7 +313,7 @@
mService.registerDeviceConfigListeners();
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
.setString(FLAG_REMOVE_OVERRIDES, PACKAGE_2 + "=123," + PACKAGE_3 + "=789")
- .setString(PACKAGE_2, "123:::true,456:::").build());
+ .setString(PACKAGE_2, "123:::true").build());
// Package 1
verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
@@ -301,14 +332,17 @@
mOverridesToRemoveConfigCaptor.getAllValues();
assertThat(configs.size()).isAtLeast(2);
assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(123L);
- assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(456L);
+ assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(456L, 789L);
// Package 3
verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
eq(PACKAGE_3));
- verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
- assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+ configs = mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.size()).isAtLeast(2);
+ assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(789L);
+ assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(123L);
}
@Test
@@ -338,9 +372,10 @@
// Package 2
verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
eq(PACKAGE_2));
- verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
- any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L, 789L);
// Package 3
verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
any(CompatibilityOverrideConfig.class), eq(PACKAGE_3));
@@ -362,10 +397,11 @@
mService.registerDeviceConfigListeners();
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
- .setString(PACKAGE_1, "123:::true,456:::")
- .setString(PACKAGE_2, "123:::true,456:::")
- .setString(PACKAGE_3, "123:::true,456:::")
- .setString(PACKAGE_4, "123:::true,456:::").build());
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456")
+ .setString(PACKAGE_1, "123:::true")
+ .setString(PACKAGE_2, "123:::true")
+ .setString(PACKAGE_3, "123:::true")
+ .setString(PACKAGE_4, "123:::true").build());
// Package 1
verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
@@ -478,12 +514,16 @@
@Test
public void packageReceiver_packageAddedIntent_appliesOverridesFromAllNamespaces()
throws Exception {
+ // We're adding the owned_change_ids flag to make sure it's ignored.
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
- .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
+ .setString(PACKAGE_1, "101:::true")
.setString(PACKAGE_2, "102:::false").build());
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(FLAG_OWNED_CHANGE_IDS, "201,202,203")
.setString(PACKAGE_3, "201:::false").build());
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(FLAG_OWNED_CHANGE_IDS, "301,302")
.setString(PACKAGE_1, "301:::true,302:::false")
.setString(PACKAGE_2, "302:::false").build());
mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
@@ -493,19 +533,18 @@
verify(mPlatformCompat, times(2)).putOverridesOnReleaseBuilds(
mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
- verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
- mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues();
assertThat(configs.get(0).overrides.keySet()).containsExactly(101L);
assertThat(configs.get(1).overrides.keySet()).containsExactly(301L, 302L);
- assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L);
}
@Test
public void packageReceiver_packageChangedIntent_appliesOverrides()
throws Exception {
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
- .setString(PACKAGE_1, "101:::true,103:::").build());
+ .setString(PACKAGE_1, "101:::true,103:::false").build());
mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
mPackageReceiver.onReceive(mMockContext,
@@ -513,10 +552,10 @@
verify(mPlatformCompat).putOverridesOnReleaseBuilds(
mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
- verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
- mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
- assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(101L);
- assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L);
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(101L,
+ 103L);
}
@Test
@@ -524,13 +563,13 @@
throws Exception {
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
.setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=103," + PACKAGE_2 + "=101")
- .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(PACKAGE_1, "101:::true,103:::false")
.setString(PACKAGE_2, "102:::false").build());
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
.setString(PACKAGE_1, "201:::false").build());
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
.setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=301," + PACKAGE_3 + "=302")
- .setString(PACKAGE_1, "301:::true,302:::false,303:::")
+ .setString(PACKAGE_1, "301:::true,302:::false,303:::true")
.setString(PACKAGE_3, "302:::false").build());
mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
@@ -539,13 +578,12 @@
verify(mPlatformCompat, times(3)).putOverridesOnReleaseBuilds(
mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
- verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
- mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues();
assertThat(configs.get(0).overrides.keySet()).containsExactly(101L);
assertThat(configs.get(1).overrides.keySet()).containsExactly(201L);
- assertThat(configs.get(2).overrides.keySet()).containsExactly(302L);
- assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(303L);
+ assertThat(configs.get(2).overrides.keySet()).containsExactly(302L, 303L);
}
@Test
@@ -573,7 +611,7 @@
throws Exception {
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
.setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
- .setString(PACKAGE_1, "101:::true,103:::").build());
+ .setString(PACKAGE_1, "101:::true,103:::false").build());
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
.setString(FLAG_OWNED_CHANGE_IDS, "201,202")
.setString(PACKAGE_1, "202:::false").build());
@@ -593,14 +631,14 @@
throws Exception {
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
.setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
- .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(PACKAGE_1, "101:::true,103:::false")
.setString(PACKAGE_2, "102:::false").build());
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
.setString(FLAG_OWNED_CHANGE_IDS, "201")
.setString(PACKAGE_3, "201:::false").build());
DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
.setString(FLAG_OWNED_CHANGE_IDS, "301,302")
- .setString(PACKAGE_1, "302:::")
+ .setString(PACKAGE_1, "302:::false")
.setString(PACKAGE_2, "301:::true").build());
mockGetApplicationInfoNotInstalled(PACKAGE_1);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index f703e2e..d0b2eda 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -906,6 +906,21 @@
}
@Test
+ public void testProviderRequest_DelayedRequest_Remove() {
+ mProvider.setProviderLocation(createLocation(NAME, mRandom));
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(60000)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+ mManager.unregisterLocationRequest(listener1);
+
+ mInjector.getAlarmHelper().incrementAlarmTime(60000);
+ assertThat(mProvider.getRequest().isActive()).isFalse();
+ }
+
+ @Test
public void testProviderRequest_SpamRequesting() {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
index 7a6110b..f17fa62 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
@@ -79,8 +79,8 @@
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
- pms.sendPackagesSuspendedForUser(
- packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+ pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
verify(pms).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
@@ -97,8 +97,8 @@
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
- pms.sendPackagesSuspendedForUser(
- packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+ pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
@@ -118,8 +118,8 @@
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
mockAllowList(packageSetting2, null)
- pms.sendPackagesSuspendedForUser(
- packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+ pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
@@ -133,6 +133,22 @@
}
}
+ @Test
+ @Throws(Exception::class)
+ fun sendPackagesSuspendModifiedForUser() {
+ pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+ verify(pms).sendPackageBroadcast(
+ eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
+ anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
+
+ var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+ assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ assertThat(modifiedUids).asList().containsExactly(
+ packageSetting1.appId, packageSetting2.appId)
+ }
+
private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
this.put(TEST_USER_ID, uids)
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 3fcd38b6..03eba9b 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -34,10 +34,10 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 4564296..0dd5c61 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -20,6 +20,8 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
@@ -1408,6 +1410,12 @@
public void testHbmVoting_forHdr() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+ final int hbmRefreshRate = 72;
+
+ // Specify limitation before starting DisplayModeDirector to avoid waiting on property
+ // propagation
+ mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hbmRefreshRate);
+
director.start(createMockSensorManager());
ArgumentCaptor<DisplayListener> captor =
@@ -1432,7 +1440,7 @@
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
- assertVoteForRefreshRate(vote, 60.f);
+ assertVoteForRefreshRate(vote, hbmRefreshRate);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
@@ -1443,6 +1451,44 @@
}
@Test
+ public void testHbmObserverGetsUpdatedRefreshRateInHbmSunlight() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+
+ final int initialRefreshRate = 60;
+ mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(initialRefreshRate);
+ director.start(createMockSensorManager());
+ assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
+ .isEqualTo(initialRefreshRate);
+
+ final int updatedRefreshRate = 90;
+ mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
+ .isEqualTo(updatedRefreshRate);
+ }
+
+ @Test
+ public void testHbmObserverGetsUpdatedRefreshRateInHbmHdr() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+
+ final int initialRefreshRate = 60;
+ mInjector.getDeviceConfig().setRefreshRateInHbmHdr(initialRefreshRate);
+ director.start(createMockSensorManager());
+ assertThat(director.getHbmObserver().getRefreshRateInHbmHdr())
+ .isEqualTo(initialRefreshRate);
+
+ final int updatedRefreshRate = 90;
+ mInjector.getDeviceConfig().setRefreshRateInHbmHdr(updatedRefreshRate);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getHbmObserver().getRefreshRateInHbmHdr())
+ .isEqualTo(updatedRefreshRate);
+ }
+
+ @Test
public void testHbmVoting_forSunlight() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
@@ -1455,11 +1501,12 @@
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
DisplayListener listener = captor.getValue();
+ final int initialRefreshRate = 60;
// Specify Limitation
when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
List.of(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
- 60.f, 60.f)));
+ initialRefreshRate, initialRefreshRate)));
// Verify that there is no HBM vote initially
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
@@ -1470,7 +1517,39 @@
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
- assertVoteForRefreshRate(vote, 60.f);
+ assertVoteForRefreshRate(vote, initialRefreshRate);
+
+ // Change refresh rate vote value through DeviceConfig, ensure it takes precedence
+ final int updatedRefreshRate = 90;
+ mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
+ .isEqualTo(updatedRefreshRate);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, updatedRefreshRate);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn HBM on again and ensure the updated vote value stuck
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, updatedRefreshRate);
+
+ // Reset DeviceConfig refresh rate, ensure vote falls back to the initial value
+ mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(0);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()).isEqualTo(0);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, initialRefreshRate);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
@@ -1518,6 +1597,63 @@
assertNull(vote);
}
+ private void setHbmAndAssertRefreshRate(
+ DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
+ when(mInjector.getBrightnessInfo(DISPLAY_ID))
+ .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode));
+ listener.onDisplayChanged(DISPLAY_ID);
+
+ final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ if (Float.isNaN(rr)) {
+ assertNull(vote);
+ } else {
+ assertVoteForRefreshRate(vote, rr);
+ }
+ }
+
+ @Test
+ public void testHbmVoting_forSunlightAndHdr() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+
+ // Specify HDR limitation before starting DisplayModeDirector to avoid waiting on property
+ // propagation
+ final int hdrRr = 60;
+ mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hdrRr);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ DisplayListener listener = captor.getValue();
+
+ // Specify Sunlight limitations
+ final float sunlightRr = 90.0f;
+ when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID))
+ .thenReturn(List.of(new RefreshRateLimitation(
+ DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, sunlightRr,
+ sunlightRr)));
+
+ // Verify that there is no HBM vote initially
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Verify all state transitions
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN);
+ }
+
@Test
public void testHbmVoting_RemovedDisplay() {
DisplayModeDirector director =
@@ -1622,6 +1758,16 @@
String.valueOf(fps));
}
+ void setRefreshRateInHbmSunlight(int fps) {
+ putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, String.valueOf(fps));
+ }
+
+ void setRefreshRateInHbmHdr(int fps) {
+ putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps));
+ }
+
void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
String thresholds = toPropertyValue(brightnessThresholds);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index f9663f2..987236c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -65,6 +65,7 @@
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@@ -1320,16 +1321,15 @@
APPROVAL_BY_COMPONENT);
ComponentName cn = ComponentName.unflattenFromString("a/a");
- service.registerSystemService(cn, 0);
- when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
- Object[] args = invocation.getArguments();
- ServiceConnection sc = (ServiceConnection) args[1];
- sc.onNullBinding(cn);
- return true;
- });
+ ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+ when(context.bindServiceAsUser(any(), captor.capture(), anyInt(), any()))
+ .thenAnswer(invocation -> {
+ captor.getValue().onNullBinding(cn);
+ return true;
+ });
service.registerSystemService(cn, 0);
- assertFalse(service.isBound(cn, 0));
+ verify(context).unbindService(captor.getValue());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f660af0..1ae219d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4011,6 +4011,80 @@
}
@Test
+ public void testApplyAdjustmentsLogged() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+ // Set up notifications that will be adjusted
+ final NotificationRecord r1 = generateNotificationRecord(
+ mTestNotificationChannel, 1, null, true);
+ r1.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ mService.addNotification(r1);
+ final NotificationRecord r2 = generateNotificationRecord(
+ mTestNotificationChannel, 2, null, true);
+ r2.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ mService.addNotification(r2);
+
+ // Third notification that's NOT adjusted, just to make sure that doesn't get spuriously
+ // logged.
+ final NotificationRecord r3 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, true);
+ r3.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ mService.addNotification(r3);
+
+ List<Adjustment> adjustments = new ArrayList<>();
+
+ // Test an adjustment that's associated with a ranking change and one that's not
+ Bundle signals1 = new Bundle();
+ signals1.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_HIGH);
+ Adjustment adjustment1 = new Adjustment(
+ r1.getSbn().getPackageName(), r1.getKey(), signals1, "",
+ r1.getUser().getIdentifier());
+ adjustments.add(adjustment1);
+
+ // This one wouldn't trigger a ranking change, but should still trigger a log.
+ Bundle signals2 = new Bundle();
+ signals2.putFloat(Adjustment.KEY_RANKING_SCORE, -0.5f);
+ Adjustment adjustment2 = new Adjustment(
+ r2.getSbn().getPackageName(), r2.getKey(), signals2, "",
+ r2.getUser().getIdentifier());
+ adjustments.add(adjustment2);
+
+ mBinderService.applyAdjustmentsFromAssistant(null, adjustments);
+ verify(mRankingHandler, times(1)).requestSort();
+
+ // Actually apply the adjustments & recalculate importance when run
+ doAnswer(invocationOnMock -> {
+ ((NotificationRecord) invocationOnMock.getArguments()[0])
+ .applyAdjustments();
+ ((NotificationRecord) invocationOnMock.getArguments()[0])
+ .calculateImportance();
+ return null;
+ }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+
+ // Now make sure that when the sort happens, we actually log the changes.
+ mService.handleRankingSort();
+
+ // Even though the ranking score change is not meant to trigger a ranking update,
+ // during this process the package visibility & canShowBadge values are changing
+ // in all notifications, so all 3 seem to trigger a ranking change. Here we check instead
+ // that scheduleSendRankingUpdate is sent and that the relevant fields have been changed
+ // accordingly to confirm the adjustments happened to the 2 relevant notifications.
+ verify(handler, times(3)).scheduleSendRankingUpdate();
+ assertEquals(IMPORTANCE_HIGH, r1.getImportance());
+ assertTrue(r2.rankingScoreMatches(-0.5f));
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED,
+ mNotificationRecordLogger.event(1));
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(2, mNotificationRecordLogger.get(1).getInstanceId());
+ }
+
+ @Test
public void testEnqueuedAdjustmentAppliesAdjustments() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addEnqueuedNotification(r);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
index 64fd19e..8a11798 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -45,6 +45,15 @@
groupInstanceId = groupId;
}
+ CallRecord(NotificationRecord r, int position, int buzzBeepBlink, InstanceId groupId) {
+ super(r, null);
+ this.position = position;
+ this.buzzBeepBlink = buzzBeepBlink;
+ wasLogged = true;
+ event = NotificationReportedEvent.NOTIFICATION_ADJUSTED;
+ groupInstanceId = groupId;
+ }
+
CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) {
super(r, null);
wasLogged = true;
@@ -75,6 +84,12 @@
}
@Override
+ public void logNotificationAdjusted(NotificationRecord r, int position, int buzzBeepBlink,
+ InstanceId groupId) {
+ mCalls.add(new CallRecord(r, position, buzzBeepBlink, groupId));
+ }
+
+ @Override
public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
mCalls.add(new CallRecord(r, event));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 51cc318..c434b13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2987,6 +2987,7 @@
mDisplayContent.setImeInputTarget(app);
// Simulate app is closing and expect the last IME is shown and IME insets is frozen.
+ mDisplayContent.mOpeningApps.clear();
app.mActivityRecord.commitVisibility(false, false);
app.mActivityRecord.onWindowsGone();
@@ -3005,6 +3006,24 @@
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
}
+ @Test
+ public void testInClosingAnimation_doNotHideSurface() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ makeWindowVisibleAndDrawn(app);
+
+ // Put the activity in close transition.
+ mDisplayContent.mOpeningApps.clear();
+ mDisplayContent.mClosingApps.add(app.mActivityRecord);
+ mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+
+ // Update visibility and call to remove window
+ app.mActivityRecord.commitVisibility(false, false);
+ app.mActivityRecord.prepareSurfaces();
+
+ // Because the app is waiting for transition, it should not hide the surface.
+ assertTrue(app.mActivityRecord.isSurfaceShowing());
+ }
+
private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 0d0cec7..d6d7f07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -31,13 +31,17 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.os.Binder;
@@ -791,4 +795,38 @@
verify(mDisplayContent.mAppTransition)
.overridePendingAppTransitionRemote(adapter, false /* sync */);
}
+
+ @Test
+ public void testTransitionGoodToGoForTaskFragments() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment changeTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .createActivityCount(1)
+ .setOrganizer(organizer)
+ .build();
+ final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setOrganizer(organizer)
+ .build();
+ changeTaskFragment.getTopMostActivity().allDrawn = true;
+ mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
+ mDisplayContent.mChangingContainers.add(changeTaskFragment);
+ spyOn(mDisplayContent.mAppTransition);
+ spyOn(emptyTaskFragment);
+
+ mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+ // Transition not ready because there is an empty non-finishing TaskFragment.
+ verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any());
+
+ doReturn(true).when(emptyTaskFragment).hasChild();
+ emptyTaskFragment.remove(false /* withTransition */, "test");
+
+ mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+ // Transition ready because the empty (no running activity) TaskFragment is requested to be
+ // removed.
+ verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
+ }
}
\ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e340217..24bbf46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1562,6 +1562,7 @@
final ActivityRecord activity = createActivityRecord(mDisplayContent);
final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent);
recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController();
// Do not rotate if the recents animation is animating on top.
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
@@ -2513,7 +2514,7 @@
assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop)));
}
- private static int getRotatedOrientation(DisplayContent dc) {
+ static int getRotatedOrientation(DisplayContent dc) {
return dc.mBaseDisplayWidth > dc.mBaseDisplayHeight
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 223dc31..a1e8ca4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -58,6 +58,7 @@
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
@@ -70,6 +71,7 @@
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
@@ -97,14 +99,20 @@
static class TestDragDropController extends DragDropController {
private Runnable mCloseCallback;
boolean mDeferDragStateClosed;
+ boolean mIsAccessibilityDrag;
TestDragDropController(WindowManagerService service, Looper looper) {
super(service, looper);
}
void setOnClosedCallbackLocked(Runnable runnable) {
- assertTrue(dragDropActiveLocked());
- mCloseCallback = runnable;
+ if (mIsAccessibilityDrag) {
+ // Accessibility does not use animation
+ assertTrue(!dragDropActiveLocked());
+ } else {
+ assertTrue(dragDropActiveLocked());
+ mCloseCallback = runnable;
+ }
}
@Override
@@ -171,6 +179,10 @@
}
latch = new CountDownLatch(1);
mTarget.setOnClosedCallbackLocked(latch::countDown);
+ if (mTarget.mIsAccessibilityDrag) {
+ mTarget.mIsAccessibilityDrag = false;
+ return;
+ }
assertTrue(awaitInWmLock(() -> latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)));
}
@@ -180,6 +192,12 @@
}
@Test
+ public void testA11yDragFlow() {
+ mTarget.mIsAccessibilityDrag = true;
+ doA11yDragAndDrop(0, ClipData.newPlainText("label", "Test"), 0, 0);
+ }
+
+ @Test
public void testPerformDrag_NullDataWithGrantUri() {
doDragAndDrop(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0);
}
@@ -436,4 +454,22 @@
appSession.kill();
}
}
+
+ private void doA11yDragAndDrop(int flags, ClipData data, float dropX, float dropY) {
+ spyOn(mTarget);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ doReturn(accessibilityManager).when(mTarget).getAccessibilityManager();
+ startA11yDrag(flags, data, () -> {
+ boolean dropped = mTarget.dropForAccessibility(mWindow.mClient, dropX, dropY);
+ mToken = mWindow.mClient.asBinder();
+ });
+ }
+
+ private void startA11yDrag(int flags, ClipData data, Runnable r) {
+ mToken = mTarget.performDrag(0, 0, mWindow.mClient,
+ flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, data);
+ assertNotNull(mToken);
+ r.run();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index d88fbee..a680cba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -41,8 +41,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -439,6 +439,22 @@
}
@Test
+ public void testCheckRotationAfterCleanup() {
+ mWm.setRecentsAnimationController(mController);
+ spyOn(mDisplayContent.mFixedRotationTransitionListener);
+ doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener)
+ .isTopFixedOrientationRecentsAnimating();
+ // Rotation update is skipped while the recents animation is running.
+ assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests
+ .getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */));
+ final int prevRotation = mDisplayContent.getRotation();
+ mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ waitHandlerIdle(mWm.mH);
+ // The display should be updated to the changed orientation after the animation is finished.
+ assertNotEquals(mDisplayContent.getRotation(), prevRotation);
+ }
+
+ @Test
public void testWallpaperHasFixedRotationApplied() {
unblockDisplayRotation(mDefaultDisplay);
mWm.setRecentsAnimationController(mController);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 89ae5ed..08312ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -341,7 +341,8 @@
verify(mMockTransaction).setWindowCrop(
mMockLeash, app.startBounds.width(), app.startBounds.height());
verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
- verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(),
+ app.startBounds.height());
finishedCaptor.getValue().onAnimationFinished();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
@@ -394,7 +395,63 @@
verify(mMockTransaction).setWindowCrop(
mMockLeash, app.startBounds.width(), app.startBounds.height());
verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
- verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(),
+ app.startBounds.height());
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
+ eq(record.mAdapter));
+ verify(mThumbnailFinishedCallback).onAnimationFinished(
+ eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
+ } finally {
+ mDisplayContent.mChangingContainers.clear();
+ }
+ }
+
+ @Test
+ public void testChangeToDifferentPosition() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mChangingContainers.add(win.mActivityRecord);
+ try {
+ final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(100, 100), null, new Rect(150, 150, 400, 400),
+ new Rect(50, 100, 150, 150));
+ assertNotNull(record.mThumbnailAdapter);
+ ((AnimationAdapter) record.mAdapter)
+ .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
+ mFinishedCallback);
+ ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
+ mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode);
+ assertEquals(new Point(100, 100), app.position);
+ assertEquals(new Rect(150, 150, 400, 400), app.sourceContainerBounds);
+ assertEquals(new Rect(50, 100, 150, 150), app.startBounds);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(mMockThumbnailLeash, app.startLeash);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setPosition(
+ mMockLeash, app.position.x + app.startBounds.left - app.screenSpaceBounds.left,
+ app.position.y + app.startBounds.top - app.screenSpaceBounds.top);
+ verify(mMockTransaction).setWindowCrop(
+ mMockLeash, app.startBounds.width(), app.startBounds.height());
+ verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(),
+ app.startBounds.height());
finishedCaptor.getValue().onAnimationFinished();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 98c1eabe..d475c46e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -76,7 +76,7 @@
@Before
public void setup() {
- mController = mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+ mController = mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController;
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
mOrganizerToken = mOrganizer.getOrganizerToken();
mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder());
@@ -284,7 +284,9 @@
@Test
public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment()
throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
mOrganizer.applyTransaction(mTransaction);
+ doReturn(true).when(mTaskFragment).isAttached();
// Throw exception if the transaction is trying to change a window that is not organized by
// the organizer.
@@ -300,8 +302,18 @@
// Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ // No lifecycle update when the TaskFragment is not recorded.
+ verify(mAtm.mRootWindowContainer, never()).resumeFocusedTasksTopActivities();
+
+ mAtm.mWindowOrganizerController.mLaunchTaskFragments
+ .put(mFragmentToken, mTaskFragment);
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
@Test
@@ -327,8 +339,11 @@
// Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
@Test
@@ -360,6 +375,8 @@
public void testApplyTransaction_enforceHierarchyChange_reparentChildren()
throws RemoteException {
mOrganizer.applyTransaction(mTransaction);
+ mController.registerOrganizer(mIOrganizer);
+ doReturn(true).when(mTaskFragment).isAttached();
// Throw exception if the transaction is trying to change a window that is not organized by
// the organizer.
@@ -375,7 +392,30 @@
// Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
+ }
+
+ @Test
+ public void testApplyTransaction_reparentActivityToTaskFragment_triggerLifecycleUpdate()
+ throws RemoteException {
+ final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
+ mOrganizer.applyTransaction(mTransaction);
+ mController.registerOrganizer(mIOrganizer);
+ mTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setCreateParentTask()
+ .setFragmentToken(mFragmentToken)
+ .build();
+ mAtm.mWindowOrganizerController.mLaunchTaskFragments
+ .put(mFragmentToken, mTaskFragment);
+ mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.appToken);
+ clearInvocations(mAtm.mRootWindowContainer);
+
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 00f3d8b..68053eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -1071,6 +1071,41 @@
verify(surfaceAnimator).setRelativeLayer(t, relativeParent, 1 /* layer */);
}
+ @Test
+ public void testAssignAnimationLayer() {
+ final WindowContainer container = new WindowContainer(mWm);
+ container.mSurfaceControl = mock(SurfaceControl.class);
+ final SurfaceAnimator surfaceAnimator = container.mSurfaceAnimator;
+ final SurfaceFreezer surfaceFreezer = container.mSurfaceFreezer;
+ final SurfaceControl relativeParent = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ spyOn(container);
+ spyOn(surfaceAnimator);
+ spyOn(surfaceFreezer);
+
+ container.setLayer(t, 1);
+ container.setRelativeLayer(t, relativeParent, 2);
+
+ // Set through surfaceAnimator if surfaceFreezer doesn't have leash.
+ verify(surfaceAnimator).setLayer(t, 1);
+ verify(surfaceAnimator).setRelativeLayer(t, relativeParent, 2);
+ verify(surfaceFreezer, never()).setLayer(any(), anyInt());
+ verify(surfaceFreezer, never()).setRelativeLayer(any(), any(), anyInt());
+
+ clearInvocations(surfaceAnimator);
+ clearInvocations(surfaceFreezer);
+ doReturn(true).when(surfaceFreezer).hasLeash();
+
+ container.setLayer(t, 1);
+ container.setRelativeLayer(t, relativeParent, 2);
+
+ // Set through surfaceFreezer if surfaceFreezer has leash.
+ verify(surfaceFreezer).setLayer(t, 1);
+ verify(surfaceFreezer).setRelativeLayer(t, relativeParent, 2);
+ verify(surfaceAnimator, never()).setLayer(any(), anyInt());
+ verify(surfaceAnimator, never()).setRelativeLayer(any(), any(), anyInt());
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 1f6065f..6626aa4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1140,6 +1140,7 @@
private int mCreateActivityCount = 0;
@Nullable
private TaskFragmentOrganizer mOrganizer;
+ private IBinder mFragmentToken;
TaskFragmentBuilder(ActivityTaskManagerService service) {
mAtm = service;
@@ -1171,10 +1172,15 @@
return this;
}
+ TaskFragmentBuilder setFragmentToken(@Nullable IBinder fragmentToken) {
+ mFragmentToken = fragmentToken;
+ return this;
+ }
+
TaskFragment build() {
SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock);
- final TaskFragment taskFragment = new TaskFragment(mAtm, null /* fragmentToken */,
+ final TaskFragment taskFragment = new TaskFragment(mAtm, mFragmentToken,
mOrganizer != null);
if (mParentTask == null && mCreateParentTask) {
mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
diff --git a/services/voiceinteraction/Android.bp b/services/voiceinteraction/Android.bp
index 702ddb1..af0eca9 100644
--- a/services/voiceinteraction/Android.bp
+++ b/services/voiceinteraction/Android.bp
@@ -18,5 +18,9 @@
name: "services.voiceinteraction",
defaults: ["platform_service_defaults"],
srcs: [":services.voiceinteraction-sources"],
- libs: ["services.core"],
+ libs: [
+ "services.core",
+ "android.hardware.power-V1-java",
+ "android.hardware.power-V1.0-java",
+ ],
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 9ea2b7b..8445ed4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -167,6 +167,16 @@
public void onStart() {
publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub);
publishLocalService(VoiceInteractionManagerInternal.class, new LocalService());
+ mAmInternal.setVoiceInteractionManagerProvider(
+ new ActivityManagerInternal.VoiceInteractionManagerProvider() {
+ @Override
+ public void notifyActivityEventChanged() {
+ if (DEBUG) {
+ Slog.d(TAG, "call notifyActivityEventChanged");
+ }
+ mServiceStub.notifyActivityEventChanged();
+ }
+ });
}
@Override
@@ -386,6 +396,14 @@
return mImpl.supportsLocalVoiceInteraction();
}
+ void notifyActivityEventChanged() {
+ synchronized (this) {
+ if (mImpl == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mImpl.notifyActivityEventChangedLocked());
+ }
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -1109,6 +1127,40 @@
}
}
+ @Override
+ public void startListeningVisibleActivityChanged(@NonNull IBinder token) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "startListeningVisibleActivityChanged without running"
+ + " voice interaction service");
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.startListeningVisibleActivityChangedLocked(token);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
+ public void stopListeningVisibleActivityChanged(@NonNull IBinder token) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "stopListeningVisibleActivityChanged without running"
+ + " voice interaction service");
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.stopListeningVisibleActivityChangedLocked(token);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
//----------------- Hotword Detection/Validation APIs --------------------------------//
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 558a9ac..52c5b6bb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -414,6 +414,44 @@
return mInfo.getSupportsLocalInteraction();
}
+ public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token);
+ }
+ if (mActiveSession == null || token != mActiveSession.mToken) {
+ Slog.w(TAG, "startListeningVisibleActivityChangedLocked does not match"
+ + " active session");
+ return;
+ }
+ mActiveSession.startListeningVisibleActivityChangedLocked();
+ }
+
+ public void stopListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
+ if (DEBUG) {
+ Slog.d(TAG, "stopListeningVisibleActivityChangedLocked: token=" + token);
+ }
+ if (mActiveSession == null || token != mActiveSession.mToken) {
+ Slog.w(TAG, "stopListeningVisibleActivityChangedLocked does not match"
+ + " active session");
+ return;
+ }
+ mActiveSession.stopListeningVisibleActivityChangedLocked();
+ }
+
+ public void notifyActivityEventChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityEventChangedLocked");
+ }
+ if (mActiveSession == null || !mActiveSession.mShown) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityEventChangedLocked not allowed on no session or"
+ + " hidden session");
+ }
+ return;
+ }
+ mActiveSession.notifyActivityEventChangedLocked();
+ }
+
public void updateStateLocked(
@NonNull Identity voiceInteractorIdentity,
@Nullable PersistableBundle options,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index cc021a9..90ccec8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -43,17 +43,20 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
+import android.hardware.power.Boost;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IVoiceInteractionSessionService;
+import android.service.voice.VisibleActivityInfo;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
import android.util.Slog;
@@ -62,21 +65,33 @@
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.am.AssistDataRequester;
import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityAssistInfo;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.PrintWriter;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
final class VoiceInteractionSessionConnection implements ServiceConnection,
AssistDataRequesterCallbacks {
- final static String TAG = "VoiceInteractionServiceManager";
+ static final String TAG = "VoiceInteractionServiceManager";
+ static final boolean DEBUG = false;
+ static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt(
+ System.getProperty("vendor.powerhal.interaction.max", "200"));
+ static final int BOOST_TIMEOUT_MS = 300;
+ // TODO: To avoid ap doesn't call hide, only 10 secs for now, need a better way to manage it
+ // in the future.
+ static final int MAX_POWER_BOOST_TIMEOUT = 10_000;
final IBinder mToken = new Binder();
final Object mLock;
@@ -104,6 +119,49 @@
ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>();
AssistDataRequester mAssistDataRequester;
+ private boolean mListeningVisibleActivity;
+ private final ScheduledExecutorService mScheduledExecutorService =
+ Executors.newSingleThreadScheduledExecutor();
+ private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
+ private final PowerManagerInternal mPowerManagerInternal;
+ private PowerBoostSetter mSetPowerBoostRunnable;
+ private final Handler mFgHandler;
+
+ class PowerBoostSetter implements Runnable {
+
+ private boolean mCanceled;
+ private final Instant mExpiryTime;
+
+ PowerBoostSetter(Instant expiryTime) {
+ mExpiryTime = expiryTime;
+ }
+
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ if (mCanceled) {
+ return;
+ }
+ // To avoid voice interaction service does not call hide to cancel setting
+ // power boost. We will cancel set boost when reaching the max timeout.
+ if (Instant.now().isBefore(mExpiryTime)) {
+ mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, BOOST_TIMEOUT_MS);
+ if (mSetPowerBoostRunnable != null) {
+ mFgHandler.postDelayed(mSetPowerBoostRunnable, POWER_BOOST_TIMEOUT_MS);
+ }
+ } else {
+ Slog.w(TAG, "Reset power boost INTERACTION because reaching max timeout.");
+ mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
+ }
+ }
+ }
+
+ void cancel() {
+ synchronized (mLock) {
+ mCanceled = true;
+ }
+ }
+ }
IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -152,7 +210,9 @@
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOps = context.getSystemService(AppOpsManager.class);
+ mFgHandler = FgThread.getHandler();
mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
@@ -255,6 +315,13 @@
mPendingHandleAssistWithoutData = topActivities;
}
}
+ // remove if already existing one.
+ if (mSetPowerBoostRunnable != null) {
+ mSetPowerBoostRunnable.cancel();
+ }
+ mSetPowerBoostRunnable = new PowerBoostSetter(
+ Instant.now().plusMillis(MAX_POWER_BOOST_TIMEOUT));
+ mFgHandler.post(mSetPowerBoostRunnable);
mCallback.onSessionShown(this);
return true;
}
@@ -420,6 +487,12 @@
} catch (RemoteException e) {
}
}
+ if (mSetPowerBoostRunnable != null) {
+ mSetPowerBoostRunnable.cancel();
+ mSetPowerBoostRunnable = null;
+ }
+ // A negative value indicates canceling previous boost.
+ mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
mCallback.onSessionHidden(this);
}
if (mFullyBound) {
@@ -432,6 +505,8 @@
}
public void cancelLocked(boolean finishTask) {
+ mListeningVisibleActivity = false;
+ mVisibleActivityInfos.clear();
hideLocked();
mCanceled = true;
if (mBound) {
@@ -505,6 +580,156 @@
mPendingShowCallbacks.clear();
}
+ void startListeningVisibleActivityChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningVisibleActivityChangedLocked");
+ }
+ mListeningVisibleActivity = true;
+ mVisibleActivityInfos.clear();
+
+ mScheduledExecutorService.execute(() -> {
+ if (DEBUG) {
+ Slog.d(TAG, "call updateVisibleActivitiesLocked from enable listening");
+ }
+ synchronized (mLock) {
+ updateVisibleActivitiesLocked();
+ }
+ });
+ }
+
+ void stopListeningVisibleActivityChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "stopListeningVisibleActivityChangedLocked");
+ }
+ mListeningVisibleActivity = false;
+ mVisibleActivityInfos.clear();
+ }
+
+ void notifyActivityEventChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityEventChangedLocked");
+ }
+ if (!mListeningVisibleActivity) {
+ if (DEBUG) {
+ Slog.d(TAG, "not enable listening visible activity");
+ }
+ return;
+ }
+ mScheduledExecutorService.execute(() -> {
+ if (DEBUG) {
+ Slog.d(TAG, "call updateVisibleActivitiesLocked from activity event");
+ }
+ synchronized (mLock) {
+ updateVisibleActivitiesLocked();
+ }
+ });
+ }
+
+ private List<VisibleActivityInfo> getVisibleActivityInfosLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "getVisibleActivityInfosLocked");
+ }
+ List<ActivityAssistInfo> allVisibleActivities =
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .getTopVisibleActivities();
+ if (DEBUG) {
+ Slog.d(TAG,
+ "getVisibleActivityInfosLocked: allVisibleActivities=" + allVisibleActivities);
+ }
+ if (allVisibleActivities == null || allVisibleActivities.isEmpty()) {
+ Slog.w(TAG, "no visible activity");
+ return null;
+ }
+ final int count = allVisibleActivities.size();
+ final List<VisibleActivityInfo> visibleActivityInfos = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ ActivityAssistInfo info = allVisibleActivities.get(i);
+ if (DEBUG) {
+ Slog.d(TAG, " : activityToken=" + info.getActivityToken()
+ + ", assistToken=" + info.getAssistToken()
+ + ", taskId=" + info.getTaskId());
+ }
+ visibleActivityInfos.add(
+ new VisibleActivityInfo(info.getTaskId(), info.getAssistToken()));
+ }
+ return visibleActivityInfos;
+ }
+
+ private void updateVisibleActivitiesLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "updateVisibleActivitiesLocked");
+ }
+ if (mSession == null) {
+ return;
+ }
+ if (!mShown || !mListeningVisibleActivity || mCanceled) {
+ return;
+ }
+ final List<VisibleActivityInfo> newVisibleActivityInfos = getVisibleActivityInfosLocked();
+
+ if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
+ updateVisibleActivitiesChangedLocked(mVisibleActivityInfos,
+ VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ mVisibleActivityInfos.clear();
+ return;
+ }
+ if (mVisibleActivityInfos.isEmpty()) {
+ updateVisibleActivitiesChangedLocked(newVisibleActivityInfos,
+ VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
+ mVisibleActivityInfos.addAll(newVisibleActivityInfos);
+ return;
+ }
+
+ final List<VisibleActivityInfo> addedActivities = new ArrayList<>();
+ final List<VisibleActivityInfo> removedActivities = new ArrayList<>();
+
+ removedActivities.addAll(mVisibleActivityInfos);
+ for (int i = 0; i < newVisibleActivityInfos.size(); i++) {
+ final VisibleActivityInfo candidateVisibleActivityInfo = newVisibleActivityInfos.get(i);
+ if (!removedActivities.isEmpty() && removedActivities.contains(
+ candidateVisibleActivityInfo)) {
+ removedActivities.remove(candidateVisibleActivityInfo);
+ } else {
+ addedActivities.add(candidateVisibleActivityInfo);
+ }
+ }
+
+ if (!addedActivities.isEmpty()) {
+ updateVisibleActivitiesChangedLocked(addedActivities,
+ VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
+ }
+ if (!removedActivities.isEmpty()) {
+ updateVisibleActivitiesChangedLocked(removedActivities,
+ VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ }
+
+ mVisibleActivityInfos.clear();
+ mVisibleActivityInfos.addAll(newVisibleActivityInfos);
+ }
+
+ private void updateVisibleActivitiesChangedLocked(
+ List<VisibleActivityInfo> visibleActivityInfos, int type) {
+ if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) {
+ return;
+ }
+ if (mSession == null) {
+ return;
+ }
+ try {
+ for (int i = 0; i < visibleActivityInfos.size(); i++) {
+ mSession.updateVisibleActivityInfo(visibleActivityInfos.get(i), type);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "updateVisibleActivitiesChangedLocked RemoteException : " + e);
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "updateVisibleActivitiesChangedLocked type=" + type + ", count="
+ + visibleActivityInfos.size());
+ }
+ }
+
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index fe7e5976..41e24dd 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -26,8 +26,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -101,9 +103,11 @@
}
mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos);
- // Sort the collection with RAN ascending order, make the ordering not matter for equals
+ // Sort the collection with RAN and then SignalMeasurementType ascending order, make the
+ // ordering not matter for equals
mSignalThresholdInfos.sort(
- Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType));
+ Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType)
+ .thenComparing(SignalThresholdInfo::getSignalMeasurementType));
return this;
}
@@ -144,7 +148,7 @@
* @return the SignalStrengthUpdateRequest object
*
* @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the
- * radio access network type in the collection is not unique
+ * signal measurement type for the same RAN in the collection is not unique
*/
public @NonNull SignalStrengthUpdateRequest build() {
return new SignalStrengthUpdateRequest(mSignalThresholdInfos,
@@ -258,14 +262,23 @@
}
/**
- * Throw IAE when the RAN in the collection is not unique.
+ * Throw IAE if SignalThresholdInfo collection is null or empty,
+ * or the SignalMeasurementType for the same RAN in the collection is not unique.
*/
private static void validate(Collection<SignalThresholdInfo> infos) {
- Set<Integer> uniqueRan = new HashSet<>(infos.size());
+ if (infos == null || infos.isEmpty()) {
+ throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty");
+ }
+
+ // Map from RAN to set of SignalMeasurementTypes
+ Map<Integer, Set<Integer>> ranToTypes = new HashMap<>(infos.size());
for (SignalThresholdInfo info : infos) {
final int ran = info.getRadioAccessNetworkType();
- if (!uniqueRan.add(ran)) {
- throw new IllegalArgumentException("RAN: " + ran + " is not unique");
+ final int type = info.getSignalMeasurementType();
+ ranToTypes.putIfAbsent(ran, new HashSet<>());
+ if (!ranToTypes.get(ran).add(type)) {
+ throw new IllegalArgumentException(
+ "SignalMeasurementType " + type + " for RAN " + ran + " is not unique");
}
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d5be4f3..9896634 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8854,7 +8854,8 @@
}
/**
- * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
+ * Set the preferred network type to global mode which includes NR, LTE, CDMA, EvDo
+ * and GSM/WCDMA.
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
@@ -8865,7 +8866,8 @@
}
/**
- * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
+ * Set the preferred network type to global mode which includes NR, LTE, CDMA, EvDo
+ * and GSM/WCDMA.
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
@@ -8873,7 +8875,7 @@
* @hide
*/
public boolean setPreferredNetworkTypeToGlobal(int subId) {
- return setPreferredNetworkType(subId, RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ return setPreferredNetworkType(subId, RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA);
}
/**